import { html, LitElement, nothing, TemplateResult } from 'lit'
import { property, state } from 'lit/decorators.js'
import { consume } from '@lit/context'
import { FolderKind } from '@api-client/core/models/kinds.js'
import type { FilesSource } from '../../../data/FilesModel.js'
import { type FileBreadcrumb, type IFile } from '@api-client/core/models/store/File.js'
import type { LiveData } from '../../../reactive/LiveData.js'
import { type IOrganization, organizationContext } from '../../../providers/organization.js'
import { type IUser, userContext } from '../../../providers/user.js'
import { api, folderFilled, schemaModel } from '../../../md/icons/Icons.js'
import type { UiMenuItemElement } from '../../../md/menu/ui-menu-item.js'

import '../../../md/icons/ui-icon.js'
import '../../../md/icon-button/ui-icon-button.js'
import '../../../md/button/ui-button.js'
import '../../../md/divider/ui-divider.js'
import '../../../md/menu/ui-menu.js'
import '../../../md/menu/ui-menu-item.js'
import '../ui-breadcrumbs.js'
import '../../data-table/filesystem-table.js'

export interface QuickApiFilesystemOpenFolderDetails {
  /**
   * The key of the folder to open.
   * When not set, it opens the root folder.
   */
  key?: string
}

export interface QuickApiFilesystemOpenFileDetail {
  /**
   * The key of the file to open.
   */
  key: string
  /**
   * The kind of the file to open.
   */
  kind: string
}

export interface QuickApiFilesystemFilesDetail {
  /**
   * The keys of files to process.
   */
  keys: string[]
}

export interface QuickApiFilesystemFileActionDetail {
  /**
   * The key of the file.
   */
  key: string
}

export interface QuickApiFilesystemFileCreateDetail {
  /**
   * The action to perform from the "add file" menu.
   */
  action: 'folder' | 'api' | 'domain' | 'import'
}

/**
 * The supported file actions.
 */
export type FileAction = 'rename' | 'move' | 'delete' | 'share' | 'open'

/**
 * A component that renders a file system view.
 * It supports listing files, navigating folders, and performing actions on files.
 *
 * The whole logic related to querying files, manipulating them, requesting breadcrumbs
 * or a parent folder is delegated to the parent component or a controller.
 *
 * @fires open-folder - Dispatched when a folder is opened.
 * @fires open-file - Dispatched when a file is opened.
 * @fires share-files - Dispatched when files are shared.
 * @fires file-rename - Dispatched when a file is renamed.
 * @fires file-move - Dispatched when a file is moved.
 * @fires file-create - Dispatched when a file create action is triggered.
 * @fires delete-files - Dispatched when files are deleted.
 * @fires scroll-end - Dispatched when the scroll end on the data table is reached.
 */
export default class QuickApiFilesystem extends LitElement {
  /**
   * What kind of files we render.
   * @attribute
   */
  @property({ type: String })
  accessor source: FilesSource = 'own'
  /**
   * The currently selected organization, if any.
   *
   * This property consumes the `organizationContext`.
   */
  @property({ type: Object, attribute: false })
  @consume({ context: organizationContext, subscribe: true })
  accessor organization: IOrganization | undefined

  /**
   * The user object to use with the file system.
   *
   * This property consumes the `userContext`.
   */
  @property({ type: Object, attribute: false })
  @consume({ context: userContext })
  accessor user: IUser | undefined

  /**
   * The list of files to render.
   */
  @property({ type: Object })
  accessor files: LiveData<IFile[]> | undefined

  /**
   * The breadcrumbs to render.
   */
  @property({ type: Object })
  accessor breadcrumbs: LiveData<FileBreadcrumb[]> | undefined

  /**
   * The parent folder of the current view.
   * It is used to compute the permissions for the current view.
   */
  @property({ type: Object })
  accessor parent: LiveData<IFile> | undefined

  /**
   * The list of currently selected items.
   */
  @state()
  accessor selectedItems: IFile[] = []

  /**
   * Dispatches the `open-folder` event.
   *
   * @param key The key of the folder to open.
   */
  protected openFolder(key?: string): void {
    this.clearSelection()
    this.dispatchEvent(
      new CustomEvent<QuickApiFilesystemOpenFolderDetails>('open-folder', {
        detail: { key },
        bubbles: true,
        composed: true,
      })
    )
  }

  /**
   * Dispatches the `open-file` event.
   *
   * @param file The file to open.
   */
  protected openFile(file: IFile): void {
    this.dispatchEvent(
      new CustomEvent<QuickApiFilesystemOpenFileDetail>('open-file', {
        detail: { key: file.key, kind: file.kind },
        bubbles: true,
        composed: true,
      })
    )
  }

  /**
   * Dispatches the `share-files` event.
   *
   * @param files The list of files to share.
   */
  protected shareFiles(files: IFile[]): void {
    this.dispatchEvent(
      new CustomEvent<QuickApiFilesystemFilesDetail>('share-files', {
        detail: { keys: files.map((file) => file.key) },
      })
    )
  }

  /**
   * Handler for the item activation event from the data table.
   * Depending on the item kind it opens the folder or the file.
   *
   * @param e The activation event.
   * @throws {Error} When the item kind is unknown.
   */
  protected itemActivateHandler(e: CustomEvent<IFile>): void {
    const item = e.detail
    const { kind } = item
    if (kind === FolderKind) {
      this.openFolder(item.key)
    } else {
      this.openFile(item)
    }
  }

  /**
   * Handler for the selection event from the data table.
   * Updates the `selectedItems` state.
   *
   * @param e The selection event.
   */
  protected handleTableSelect(e: CustomEvent<{ selectedItems: IFile[] }>): void {
    this.selectedItems = [...e.detail.selectedItems]
  }

  /**
   * Clears the current selection.
   */
  clearSelection(): void {
    this.selectedItems = []
  }

  /**
   * Shares the currently selected files.
   */
  shareSelection(): void {
    const { selectedItems } = this
    this.shareFiles(selectedItems)
  }

  /**
   * Removes files from the selection.
   * @param keys The keys of the files to remove from the selection.
   */
  removeFromSelection(keys: string[]): void {
    const { selectedItems } = this
    this.selectedItems = selectedItems.filter((item) => !keys.some((key) => key === item.key))
  }

  /**
   * Logic to handle file actions from the data table (e.g. rename, delete, share, open).
   *
   * @param e The file action event.
   */
  protected handleFileAction(e: CustomEvent<{ key: string; action: string }>): void {
    const { key } = e.detail
    const action = e.detail.action as FileAction

    switch (action) {
      case 'rename':
      case 'move':
        this.dispatchEvent(
          new CustomEvent<QuickApiFilesystemFileActionDetail>(`file-${action}`, {
            detail: { key },
            bubbles: true,
            composed: true,
          })
        )
        break
      case 'delete':
        this.deleteFiles([key])
        break
      case 'share':
      case 'open': {
        const item = this.files?.value?.find((item) => item.key === key)
        if (!item) {
          return
        }
        if (action === 'share') {
          this.shareFiles([item])
        } else if (action === 'open') {
          if (item.kind !== FolderKind) {
            this.openFile(item)
          } else {
            this.openFolder(key)
          }
        }
        break
      }
      default:
      // Unknown action
    }
  }

  /**
   * Handler for the "add file" menu selection.
   * Dispatches the `file-create` event.
   *
   * @param e The menu selection event.
   */
  protected handleAddMenuSelect(e: CustomEvent<{ item: UiMenuItemElement }>): void {
    const action = e.detail.item.value as QuickApiFilesystemFileCreateDetail['action'] | undefined
    if (!action || !['folder', 'api', 'domain', 'import'].includes(action)) {
      return
    }
    this.dispatchEvent(
      new CustomEvent<QuickApiFilesystemFileCreateDetail>('file-create', {
        detail: { action },
      })
    )
  }

  /**
   * Handler for the breadcrumbs navigation event.
   * Opens the folder corresponding to the breadcrumb.
   *
   * @param e The breadcrumb navigation event.
   */
  protected handleBreadcrumbsNavigate(e: CustomEvent<FileBreadcrumb>): void {
    const { key } = e.detail
    this.openFolder(key)
  }

  /**
   * Deletes the currently selected files.
   */
  protected handleDeleteSelected(): void {
    const { selectedItems } = this
    this.deleteFiles(selectedItems.map((item) => item.key))
  }

  /**
   * Deletes a list of files identified by their keys.
   * Dispatches the `delete-files` event and removes the files from the selection.
   *
   * @param keys The keys of the files to delete.
   */
  async deleteFiles(keys: string[]): Promise<void> {
    this.dispatchEvent(
      new CustomEvent<QuickApiFilesystemFilesDetail>('delete-files', {
        detail: { keys },
      })
    )
    this.removeFromSelection(keys)
  }

  protected handleScrollEnd(): void {
    this.dispatchEvent(new CustomEvent('scroll-end'))
  }

  protected override render(): TemplateResult {
    const { selectedItems, files } = this
    const own = this.source === 'own'
    const ariaLabel = own ? 'Your files' : 'Shared files'
    const content = html`
      <div class="section-header">
        ${selectedItems && selectedItems.length ? this.renderSelection(selectedItems) : this.renderHeader()}
      </div>
      <filesystem-table
        .oid="${this.organization?.key}"
        .user="${this.user}"
        .items="${files}"
        selection="multi"
        active
        ariaLabel="${ariaLabel}"
        .selectedItems="${selectedItems}"
        @activate="${this.itemActivateHandler}"
        @select="${this.handleTableSelect}"
        @rowaction="${this.handleFileAction}"
        @scrollend="${this.handleScrollEnd}"
        class="table"
      >
        ${own
          ? html`<p slot="empty" class="empty">You have no files.</p>`
          : html`<p slot="empty" class="empty">You have no shared files.</p>`}
      </filesystem-table>
    `
    return content
  }

  protected renderSelection(selected: IFile[]): TemplateResult {
    const { length } = selected
    return html`
      <div class="file-selection-info">
        <ui-icon-button @click="${this.clearSelection}" title="Clear selection" aria-label="Clear selection">
          <ui-icon>close</ui-icon>
        </ui-icon-button>
        <span class="selection-label body-large">${length} selected</span>
        <ui-icon-button
          @click="${this.shareSelection}"
          title="Share selected"
          aria-label="Share selected"
          ?disabled="${length !== 1}"
        >
          <ui-icon>person_add</ui-icon>
        </ui-icon-button>
        <!-- <ui-icon-button>
          <ui-icon>drive_file_move</ui-icon>
        </ui-icon-button> -->
        <ui-icon-button
          @click="${this.handleDeleteSelected}"
          title="Move selected to trash"
          aria-label="Move selected to trash"
        >
          <ui-icon>delete</ui-icon>
        </ui-icon-button>
      </div>
    `
  }

  protected renderHeader(): TemplateResult {
    return html` ${this.renderBreadcrumbs()}${this.renderAddFileMenu()} `
  }

  protected renderBreadcrumbs(): TemplateResult {
    const rootName = this.source === 'own' ? 'Files' : 'Shared'
    return html`
      <div class="breadcrumbs">
        <ui-breadcrumbs
          .breadcrumbs="${this.breadcrumbs}"
          root-name="${rootName}"
          @navigate="${this.handleBreadcrumbsNavigate}"
        ></ui-breadcrumbs>
      </div>
    `
  }

  protected renderAddFileMenu(): TemplateResult | typeof nothing {
    const own = this.source === 'own'
    if (!own) {
      if (this.parent) {
        const caps = this.parent.value?.capabilities
        if (!caps?.canAddChildren) {
          return nothing
        }
      } else {
        return nothing
      }
    }
    return html`
      <span class="menu-area">
        <ui-button color="filled" id="new-file-anchor" trailingicon popoverTarget="new-file-menu">
          Add
          <ui-icon slot="icon">arrow_drop_down</ui-icon>
        </ui-button>
        <ui-menu id="new-file-menu" popover="auto" @select="${this.handleAddMenuSelect}">
          <ui-menu-item value="folder">
            <div>Folder</div>
            <div slot="supporting-text">A folder for your domains</div>
            <ui-icon slot="start">${folderFilled}</ui-icon>
          </ui-menu-item>
          <ui-menu-item value="domain">
            <div>Domain model</div>
            <div slot="supporting-text">Define your data</div>
            <ui-icon slot="start">${schemaModel}</ui-icon>
          </ui-menu-item>
          <ui-menu-item value="api">
            <div>API</div>
            <div slot="supporting-text">Make data actionable</div>
            <ui-icon slot="start">${api}</ui-icon>
          </ui-menu-item>
          <ui-divider role="separator"></ui-divider>
          <ui-menu-item value="import">
            <div>Import data model</div>
            <div slot="supporting-text">Use well-know domains</div>
            <ui-icon slot="start">download_2</ui-icon>
          </ui-menu-item>
        </ui-menu>
      </span>
    `
  }
}
