/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as vscode from 'vscode'
import { getNonce } from './utilities/getNonce'
import { printChannelOutput } from './extension'
import { newTestCaseInput } from './addNewTest'
import { AppConstants } from './utilities/constants'
import { getTestCaseDisplayData } from './utilities/tdmlXmlUtils'

export class TDMLProvider implements vscode.CustomTextEditorProvider {
  public static register(context: vscode.ExtensionContext): vscode.Disposable {
    const provider = new TDMLProvider(context)
    const providerRegistration = vscode.window.registerCustomEditorProvider(
      TDMLProvider.viewType,
      provider
    )
    printChannelOutput('TDML Editor custom editor provider registered.', true)
    return providerRegistration
  }

  private static readonly viewType = AppConstants.viewTypeId
  private registered = false
  private currentPanel: vscode.WebviewPanel | undefined = undefined

  constructor(private readonly context: vscode.ExtensionContext) {}

  public async resolveCustomTextEditor(
    document: vscode.TextDocument,
    webviewPanel: vscode.WebviewPanel,
    _token: vscode.CancellationToken
  ): Promise<void> {
    this.currentPanel = webviewPanel
    webviewPanel.webview.options = {
      enableScripts: true,
      localResourceRoots: [
        vscode.Uri.joinPath(this.context.extensionUri, 'src/tdmlEditor/media'),
        vscode.Uri.joinPath(
          this.context.extensionUri,
          'dist/views/tdmlEditor/webview'
        ),
      ],
    }
    webviewPanel.webview.html = this._getWebviewContent(webviewPanel.webview)
    webviewPanel.onDidChangeViewState((e) => {
      this.currentPanel = e.webviewPanel
    })

    try {
      printChannelOutput(document.uri.toString(), true)
      if (!this.registered) {
        this.registered = true
        let deleteCommand = vscode.commands.registerCommand(
          AppConstants.deleteTestCommand,
          () => {
            this.currentPanel?.webview.postMessage({
              type: 'delete',
            })
          }
        )

        let addCommand = vscode.commands.registerCommand(
          AppConstants.addNewTestCommand,
          () => {
            // get all the inputs we need
            const inputs = newTestCaseInput(this.context)
            // then do something with them
            inputs.then((result) => {
              this.currentPanel?.webview.postMessage({
                type: 'add',
                testCaseName: result.testName,
                testCaseModel: result.testDesc,
                testCaseDescription: result.testModel,
                dataDocuments: result.dfdlInfoset,
                dfdlInfosets: result.dataDocs,
              })
            })
          }
        )

        let openInTextEditorCommand = vscode.commands.registerCommand(
          AppConstants.openInTextEditorCommand,
          () => {
            printChannelOutput('openInTextEditor command called', true)
            vscode.commands.executeCommand(
              'workbench.action.reopenTextEditor',
              document?.uri
            )
          }
        )

        this.context.subscriptions.push(openInTextEditorCommand)
        this.context.subscriptions.push(deleteCommand)
        this.context.subscriptions.push(addCommand)
      }
    } catch (e) {
      console.log(e)
    }

    async function updateWebview() {
      webviewPanel.webview.postMessage({
        type: 'update',
        text: await getTestCaseDisplayData(document.getText()),
      })
    }

    const changeDocumentSubscription = vscode.workspace.onDidChangeTextDocument(
      (e) => {
        if (e.document.uri.toString() === document.uri.toString()) {
          updateWebview()
        }
      }
    )

    webviewPanel.onDidDispose(() => {
      changeDocumentSubscription.dispose()
    })

    webviewPanel.webview.onDidReceiveMessage((e) => {
      switch (e.type) {
        case 'update':
          this.updateTextDocument(document, e.json)
          return
        case 'log':
          printChannelOutput(e.message, true)
          return
        case 'error':
          printChannelOutput(e.message, true)
          vscode.window.showErrorMessage(e.message)
          return
        case 'info':
          printChannelOutput(e.message, true)
          vscode.window.showInformationMessage(e.message)
          return
        case 'add':
          vscode.commands.executeCommand(AppConstants.addNewTestCommand)
          return
      }
    })

    updateWebview()
  }

  private async updateTextDocument(document: vscode.TextDocument, json: any) {
    const edit = new vscode.WorkspaceEdit()

    return vscode.workspace.applyEdit(edit)
  }

  private _getWebviewContent(webview: vscode.Webview) {
    const webviewUri = webview.asWebviewUri(
      vscode.Uri.joinPath(
        this.context.extensionUri,
        'dist/views/tdmlEditor/webview',
        'webview.js'
      )
    )
    const nonce = getNonce()

    return /*html*/ `
              <!DOCTYPE html>
              <html lang="en">
                <head>
                  <meta charset="UTF-8">
                  <meta name="viewport" content="width=device-width, initial-scale=1.0">
                  <meta
                    http-equiv="Content-Security-Policy"
                    content="default-src 'none'; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}'; style-src ${webview.cspSource} 'nonce-${nonce}'; style-src-elem ${webview.cspSource} 'unsafe-inline'; font-src ${webview.cspSource};"
                  />
                  <link rel="stylesheet" nonce="${nonce}">
                </head>
                <body>
                  <h2>TDML Test Cases</h2><br>
                  <vscode-data-grid id="TDML-table" aria-label="Basic" generate-header="sticky" aria-label="Sticky Header"></vscode-data-grid>
                  <script type="module" nonce="${nonce}" src="${webviewUri}"></script>
                </body>
              </html>
            `
  }
}
