import { CSSResult, html, nothing, TemplateResult } from 'lit'
import { property, state } from 'lit/decorators.js'
import { live } from 'lit/directives/live.js'
import { Certificate, type CertificateType } from '@api-client/core/models/ClientCertificate.js'
import { CertificateFile } from '@api-client/core/models/CertificateFile.js'
import { Events } from '../../events/Events.js'
import ApiElement from '../ApiElement.js'
import styles from './CertificateAdd.styles.js'
import { getFileName } from '../../lib/files/Utils.js'
import type { ISegmentedButtonSelectionDetail } from '../../md/segmented-button/internals/SegmentedButtonsSet.js'
import type SegmentedButton from '../../md/segmented-button/internals/SegmentedButton.js'
import type Input from '../../md/input/Input.js'
// import type { IFileFilter, IOpenFileDialogInit } from '../../bindings/base/FileBindings.js'
import { SnackNotifications } from '../../md/notification/SnackNotifications.js'
import '../../md/segmented-button/ui-segmented-button.js'
import '../../md/segmented-button/ui-segmented-button-set.js'
import '../../md/text-field/ui-filled-text-field.js'
import '../../md/button/ui-button.js'
import '../../md/icons/ui-icon.js'
import '../../md/progress/ui-progress.js'

interface CertificateInfo {
  fileName: string
  contents: ArrayBuffer
}

/**
 * An element that renders a certificate import UI.
 *
 * Notes for implementation:
 * - `p12` is a single file, may have a passphrase
 * - `pem` is 2 files: the certificate and the key
 * - `pem` may have a `passphrase` on the key but not on the certificate.
 */
export default class CertificateAdd extends ApiElement {
  static override get styles(): CSSResult[] {
    return styles
  }

  /**
   * The type of the certificate to import.
   * @attribute
   */
  @property({ type: String }) accessor importType: CertificateType = 'pem'

  /**
   * The parent folder where to insert the imported certificate.
   * @attribute
   */
  @property({ type: String }) accessor folder: string | undefined

  /**
   * The current workspace for the file.
   * @attribute
   */
  @property({ type: String }) accessor space: string | undefined

  /**
   * When an IO operation is in progress.
   */
  @state() accessor working: boolean | undefined

  @state() accessor certificateFile: CertificateInfo | undefined

  @state() accessor keyFile: CertificateInfo | undefined

  /**
   * The password for the certificate
   */
  certificatePassword?: string

  /**
   * The password for the key.
   */
  keyPassword?: string

  /**
   * The user entered certificate name.
   */
  protected certificateName?: string

  get hasKeyImport(): boolean {
    return this.importType === 'pem'
  }

  get hasCertificatePasswordInput(): boolean {
    return this.importType === 'p12'
  }

  protected handleViewSelection(e: CustomEvent<ISegmentedButtonSelectionDetail>): void {
    if (!e.detail.selected) {
      return
    }
    const button = e.detail.button as SegmentedButton
    const type = button.dataset.value as CertificateType | undefined
    if (type) {
      this.importType = type
    }
  }

  protected handleNameChange(e: Event): void {
    const input = e.target as Input
    this.certificateName = input.value
  }

  protected handleCertificatePasswordChange(e: Event): void {
    const input = e.target as Input
    this.certificatePassword = input.value
  }

  protected handleKeyPasswordChange(e: Event): void {
    const input = e.target as Input
    this.keyPassword = input.value
  }

  protected handleSelectCertificate(): void {
    this.selectCertificateFLow()
  }

  protected handleSelectKey(): void {
    this.selectKeyFLow()
  }

  async selectCertificateFLow(): Promise<void> {
    throw new Error(`Not yet implemented`)
    // this.working = true
    // const options: IOpenFileDialogInit = {
    //   multiple: false,
    //   title: 'Select a certificate file',
    //   filters: [
    //     {
    //       description: 'Certificate files',
    //       accept: {},
    //     },
    //   ],
    // }
    // const filters = options.filters as IFileFilter[]
    // if (this.importType === 'p12') {
    //   filters[0].accept = {
    //     'application/x-pkcs12': ['.p12', '.pfx'],
    //   }
    // } else {
    //   filters[0].accept = {
    //     'application/x-pem-file': ['.pem', '.crt', '.cer', '.key'],
    //   }
    // }
    // const pickResult = await Events.Filesystem.requestOpenFile(options, this)
    // if (!pickResult || pickResult.canceled) {
    //   this.working = false
    //   return
    // }
    // const [path] = pickResult.filePath as string[]
    // const readResult = await Events.Filesystem.readFile(path, { returnType: 'buffer', dispose: true }, this)
    // if (!readResult) {
    //   this.working = false
    //   return
    // }

    // const fileName = getFileName.getFileName(path)
    // this.certificateFile = {
    //   fileName,
    //   contents: readResult as ArrayBuffer,
    // }
    // this.working = false
  }

  async selectKeyFLow(): Promise<void> {
    throw new Error(`Not yet implemented`)
    // this.working = true
    // const options: IOpenFileDialogInit = {
    //   multiple: false,
    //   title: 'Select a key file',
    //   filters: [
    //     {
    //       description: 'Certificate key files',
    //       accept: {
    //         'application/x-pem-file': ['.pem', '.crt', '.cer', '.key'],
    //       },
    //     },
    //   ],
    // }
    // const pickResult = await Events.Filesystem.requestOpenFile(options, this)
    // if (!pickResult || pickResult.canceled) {
    //   this.working = false
    //   return
    // }
    // const [path] = pickResult.filePath as string[]
    // const readResult = await Events.Filesystem.readFile(path, { returnType: 'buffer', dispose: true }, this)
    // if (!readResult) {
    //   this.working = false
    //   return
    // }

    // const fileName = getFileName.getFileName(path)
    // this.keyFile = {
    //   fileName,
    //   contents: readResult as ArrayBuffer,
    // }
    // this.working = false
  }

  protected isValidDropTarget(dt: DataTransfer): boolean {
    const types = Array.from(dt.types)
    return types.includes('Files')
  }

  protected handleFileDragEnter(e: DragEvent): void {
    const dt = e.dataTransfer as DataTransfer
    if (!this.isValidDropTarget(dt)) {
      return
    }
    e.preventDefault()
    dt.dropEffect = 'copy'
    const node = e.currentTarget as HTMLElement
    node.classList.add('dragover')
  }

  protected handleFileDragLeave(e: DragEvent): void {
    const dt = e.dataTransfer as DataTransfer
    if (!this.isValidDropTarget(dt)) {
      return
    }
    e.preventDefault()
    dt.dropEffect = 'none'
    const node = e.currentTarget as HTMLElement
    node.classList.remove('dragover')
  }

  protected handleFileDragOver(e: DragEvent): void {
    const dt = e.dataTransfer as DataTransfer
    if (!this.isValidDropTarget(dt)) {
      return
    }
    e.preventDefault()
    const node = e.currentTarget as HTMLElement
    node.classList.add('dragover')
  }

  protected handleFileDrop(e: DragEvent): void {
    const dt = e.dataTransfer as DataTransfer
    e.preventDefault()
    const node = e.currentTarget as HTMLElement
    node.classList.remove('dragover')
    const file = dt.files[0]
    if (!file) {
      return
    }
    const { kind } = node.dataset
    if (!kind) {
      return
    }
    this.processDroppedFile(file, kind as 'certificate' | 'key')
  }

  async processDroppedFile(file: File, kind: 'certificate' | 'key'): Promise<void> {
    this.working = true
    const buff = await file.arrayBuffer()
    const fileName = getFileName(file.name)
    if (kind === 'certificate') {
      this.certificateFile = {
        fileName,
        contents: buff,
      }
    } else if (kind === 'key') {
      this.keyFile = {
        fileName,
        contents: buff,
      }
    }
    this.working = false
  }

  protected handleCancel(): void {
    this.dispatchEvent(new Event('close'))
  }

  protected handleImport(): void {
    this.submit()
  }

  async submit(): Promise<void> {
    const invalid = this.reportValidity()
    if (invalid) {
      return
    }
    const { certificateFile, certificateName, certificatePassword, keyFile, keyPassword, folder } = this
    const cert =
      this.importType === 'p12'
        ? Certificate.fromP12(certificateFile?.contents as ArrayBuffer, certificateName as string, certificatePassword)
        : Certificate.fromPem(
            certificateFile?.contents as ArrayBuffer,
            keyFile?.contents as ArrayBuffer,
            certificateName as string,
            keyPassword
          )
    const file = CertificateFile.fromCertificate(cert)
    await Events.Store.File.create(file.toJSON(), cert.toJSON(), { parent: folder }, this)
    this.handleCancel()
  }

  reportValidity(): boolean {
    const inputs = Array.from((this.shadowRoot as ShadowRoot).querySelectorAll('ui-filled-text-field'))
    let invalid = false
    inputs.forEach((input) => {
      const inputResult = input.reportValidity()
      if (!inputResult) {
        invalid = true
      }
    })

    if (!invalid) {
      if (!this.certificateFile) {
        invalid = true
        SnackNotifications.notify(`Certificate file is required.`)
      }
      if (!invalid && this.hasKeyImport && !this.keyFile) {
        invalid = true
        SnackNotifications.notify(`The key file is required.`)
      }
    }
    return invalid
  }

  protected override render(): TemplateResult {
    return html`
      <div class="form">
        ${this.renderProgress()} ${this.renderViewToggle()} ${this.renderNameInput()} ${this.renderCertificateFile()}
        ${this.renderCertificatePasswordInput()} ${this.renderKeyFile()} ${this.renderKeyPasswordInput()}
        ${this.renderImportButton()}
      </div>
    `
  }

  protected renderViewToggle(): TemplateResult {
    const { importType } = this
    return html`
      <div class="view-toggle">
        <ui-segmented-button-set @select="${this.handleViewSelection}">
          <ui-segmented-button
            .selected="${importType === 'pem'}"
            title="Import a certificate in PEM format"
            data-value="pem"
            >PEM</ui-segmented-button
          >
          <ui-segmented-button
            .selected="${importType === 'p12'}"
            title="Import a bundled PFX or PKCS12 encoded private key and certificate"
            data-value="p12"
            >PFX or PKCS12</ui-segmented-button
          >
        </ui-segmented-button-set>
      </div>
    `
  }

  protected renderNameInput(): TemplateResult {
    const { certificateName = '' } = this
    return html`
      <div class="form-input">
        <ui-filled-text-field
          required
          name="name"
          .value="${live(certificateName)}"
          label="Certificate name"
          @change="${this.handleNameChange}"
          invalidText="The name is required"
        ></ui-filled-text-field>
      </div>
    `
  }

  protected renderCertificateFile(): TemplateResult {
    const { certificateFile } = this
    return html`
      <div
        class="cert-file"
        data-kind="certificate"
        @dragenter="${this.handleFileDragEnter}"
        @dragleave="${this.handleFileDragLeave}"
        @dragover="${this.handleFileDragOver}"
        @drop="${this.handleFileDrop}"
      >
        <p class="body-medium">Select or drop a certificate file here.</p>
        <ui-button color="tonal" @click="${this.handleSelectCertificate}" class="select-button"
          >Select certificate file</ui-button
        >
        ${certificateFile
          ? html`<span class="cert-info body-medium">${certificateFile.fileName}</span>`
          : html`<span class="missing body-medium"
              ><ui-icon role="presentation" icon="warning" class="missing-icon"></ui-icon>Certificate is not
              selected</span
            >`}
      </div>
    `
  }

  protected renderCertificatePasswordInput(): TemplateResult | typeof nothing {
    const { certificatePassword = '' } = this
    if (!this.hasCertificatePasswordInput) {
      return nothing
    }
    return html`
      <div class="form-input">
        <ui-filled-text-field
          name="certificatePassword"
          type="password"
          .value="${live(certificatePassword)}"
          label="Certificate password"
          @change="${this.handleCertificatePasswordChange}"
          supportingText="Optional password for the certificate"
        ></ui-filled-text-field>
      </div>
    `
  }

  protected renderKeyFile(): TemplateResult | typeof nothing {
    if (!this.hasKeyImport) {
      return nothing
    }
    const { keyFile } = this
    return html`
      <div
        class="cert-file"
        data-kind="key"
        @dragenter="${this.handleFileDragEnter}"
        @dragleave="${this.handleFileDragLeave}"
        @dragover="${this.handleFileDragOver}"
        @drop="${this.handleFileDrop}"
      >
        <p class="body-medium">Select or drop a key file here.</p>
        <ui-button color="tonal" @click="${this.handleSelectKey}" class="select-button">Select key file</ui-button>
        ${keyFile
          ? html`<span class="cert-info body-medium">${keyFile.fileName}</span>`
          : html`<span class="missing body-medium"
              ><ui-icon role="presentation" icon="warning" class="missing-icon"></ui-icon>Key is not selected</span
            >`}
      </div>
    `
  }

  protected renderKeyPasswordInput(): TemplateResult | typeof nothing {
    if (!this.hasKeyImport) {
      return nothing
    }
    const { keyPassword = '' } = this
    return html`
      <div class="form-input">
        <ui-filled-text-field
          name="keyPassword"
          type="password"
          .value="${live(keyPassword)}"
          label="Key password"
          @change="${this.handleKeyPasswordChange}"
          supportingText="Optional password for the key"
        ></ui-filled-text-field>
      </div>
    `
  }

  protected renderImportButton(): TemplateResult {
    return html`
      <div class="form-action">
        <ui-button color="filled" @click="${this.handleImport}">Import</ui-button>
        <ui-button color="text" @click="${this.handleCancel}">Cancel</ui-button>
      </div>
    `
  }

  protected renderProgress(): TemplateResult | typeof nothing {
    if (!this.working) {
      return nothing
    }
    return html`<ui-progress indeterminate class="working"></ui-progress>`
  }
}
