import { html, PropertyValues, TemplateResult } from 'lit'
import { DataTable } from '../DataTable.js'
import { styleMap } from 'lit/directives/style-map.js'
import type { DataCatalogSchemaWithVersion } from '@api-client/core/models/DataCatalog.js'
import { schemaModel as schemaIcon } from '../../../md/icons/Icons.js'
import type { UiListSelection } from '../../../md/list/internals/List.js'
import { property } from 'lit/decorators.js'

/**
 * Represents a selection in the data catalog browser.
 */
export interface DataCatalogTableSelectionDetail {
  /**
   * The schema that was selected.
   * When not set, the selection was cleared.
   */
  item?: DataCatalogSchemaWithVersion
  /**
   * The version of the schema that was selected.
   * When not set, the selection was cleared.
   */
  version?: string
}

/**
 * @fires CustomEvent<DataCatalogTableSelectionDetail> select - Dispatched when the selection in the table changes.
 */
export default class DataCatalogTable extends DataTable<DataCatalogSchemaWithVersion> {
  // @ts-expect-error This is to explicitly declare the type as LitPlugin does not handle this situation.
  declare items: LiveData<DataCatalogSchemaWithVersion[]> | undefined

  /**
   * When the user makes a selection, this map will contain the selected version for a schema.
   */
  selectedVersions = new Map<string, string>()

  /**
   * When set, the table will render additional column with a button to directly select the item.
   * Note, the row selection will not be available in this case.
   * @attribute
   */
  @property({ type: Boolean, reflect: true }) accessor directSelect = false

  constructor() {
    super()
    if (this.directSelect) {
      this.selection = 'none'
    } else {
      this.selection = 'single'
      this.active = true
    }
  }

  protected override willUpdate(cp: PropertyValues<this>): void {
    if (cp.has('directSelect')) {
      if (this.directSelect) {
        this.selection = 'none'
        this.active = false
        this.clearSelection()
      } else {
        this.selection = 'single'
        this.active = true
      }
    }
    if (cp.has('items')) {
      this.clearSelection()
    }
  }

  protected clearSelection(): void {
    this.selectedVersions.clear()
    const hasSelected = this.selectedItems[0]
    this.selectedItems = []
    if (hasSelected) {
      this.notifySelect([])
    }
  }

  protected handleVersionSelect(e: CustomEvent<UiListSelection>): void {
    const target = e.detail.item
    const value = target.dataset.value
    const key = target.dataset.key
    if (!value || !key) {
      return
    }
    this.selectedVersions.set(key, value)
    const popover = this.shadowRoot?.getElementById(`select-${key}`) as HTMLElement | null
    if (popover) {
      popover.hidePopover()
    }
    const anchor = this.shadowRoot?.querySelector(`[popovertarget="select-${key}"]`) as HTMLElement | null
    if (anchor) {
      anchor.focus()
    }
    this.requestUpdate()
    const selected = this.selectedItems[0]
    if (selected && selected.key === key) {
      // if the selected item is the one that was just updated, notify the parent
      this.notifySelect([selected])
    }
  }

  protected override notifySelect(selectedItems: DataCatalogSchemaWithVersion[]): void {
    const selected = selectedItems[0]
    const detail: DataCatalogTableSelectionDetail = {}
    if (selected) {
      detail.item = selected
      detail.version = this.selectedVersions.get(selected.key) || selected.versions[0].version
    }
    this.dispatchEvent(
      new CustomEvent('select', {
        detail,
      })
    )
  }

  override renderHeader(): TemplateResult[] {
    const result: TemplateResult[] = [
      this.renderHeaderCell('Name', 0),
      this.renderHeaderCell('Version', 1, { delegateFocus: true }),
      this.renderHeaderCell('Lifecycle', 2),
      this.renderHeaderCell('Organization', 3),
      this.renderHeaderCell('Published', 4),
    ]
    if (this.directSelect) {
      result.push(this.renderHeaderCell('', 5, { classes: ['actions'], delegateFocus: true, ariaLabel: 'Actions' }))
    }
    return result
  }

  override renderRowValues(item: DataCatalogSchemaWithVersion, index: number): TemplateResult[] {
    const result: TemplateResult[] = [
      this.#nameCell(item, index, 0),
      this.#versionCell(item, index, 1),
      this.#lifecycleCell(item, index, 2),
      this.#organizationCell(item, index, 3),
      this.#publishedCell(item, index, 4),
    ]
    if (this.directSelect) {
      result.push(this.#actionCell(item, index, 5))
    }
    return result
  }

  #nameCell(item: DataCatalogSchemaWithVersion, row: number, col: number): TemplateResult {
    const content = html`<ui-icon class="cell-icon" role="presentation">${schemaIcon}</ui-icon> ${item.name}`
    return this.renderCell(content, row, col, { classes: ['primary-cell'], isPrimary: true })
  }

  #versionCell(item: DataCatalogSchemaWithVersion, row: number, col: number): TemplateResult {
    if (item.versions.length === 1) {
      return this.renderCell(item.versions[0].version, row, col, { classes: ['version'] })
    }
    const selectedVersion = this.selectedVersions.get(item.key) || item.versions[0].version
    const content = html`<ui-button
        size="xs"
        color="text"
        class="version-label"
        trailingIcon
        popovertarget="select-${item.key}"
        style="${styleMap({ 'anchor-name': `--select-${item.key}` })}"
        part="version-button"
        ><ui-icon slot="icon">arrow_drop_down</ui-icon>${selectedVersion}</ui-button
      ><ui-listbox
        popover="auto"
        id="select-${item.key}"
        class="version-listbox"
        role="listbox"
        aria-label="Select version"
        style="${styleMap({ 'position-anchor': `--select-${item.key}` })}"
        part="version-listbox"
        @select="${this.handleVersionSelect}"
      >
        ${item.versions.map(
          (version) =>
            html`<ui-list-item
              class="version-item"
              role="option"
              lines="two"
              data-value="${version.version}"
              data-key="${item.key}"
              aria-selected="${version.version === selectedVersion}"
              part="version-item"
            >
              ${version.version}
              <span slot="supporting-text">${version.lifecycle}</span>
            </ui-list-item>`
        )}
      </ui-listbox>`
    return this.renderCell(content, row, col, { classes: ['version'], delegateFocus: true })
  }

  #lifecycleCell(item: DataCatalogSchemaWithVersion, row: number, col: number): TemplateResult {
    const selectedVersion = this.selectedVersions.get(item.key) || item.versions[0].version
    const info = item.versions.find((v) => v.version === selectedVersion)
    if (!info) {
      return this.renderCell('Unknown', row, col, { classes: ['lifecycle'] })
    }
    const lifecycle = info.lifecycle.charAt(0).toUpperCase() + info.lifecycle.slice(1)
    return this.renderCell(lifecycle, row, col, {
      classes: ['lifecycle'],
    })
  }

  #organizationCell(item: DataCatalogSchemaWithVersion, row: number, col: number): TemplateResult {
    return this.renderCell(item.organization, row, col, {
      classes: ['organization'],
    })
  }

  #publishedCell(item: DataCatalogSchemaWithVersion, row: number, col: number): TemplateResult {
    return this.renderCell(html`<ui-date-time date="${item.publishedAt}"></ui-date-time>`, row, col, {
      classes: ['published'],
    })
  }

  #actionCell(item: DataCatalogSchemaWithVersion, row: number, col: number): TemplateResult {
    return this.renderCell(
      html`<ui-button
        size="xs"
        color="outlined"
        class="select-button"
        @click="${() => this.notifySelect([item])}"
        aria-label="Select ${item.name}"
        >Select</ui-button
      >`,
      row,
      col,
      { classes: ['actions'], delegateFocus: true }
    )
  }
}
