import { html, TemplateResult, PropertyValues } from 'lit'
import { property } from 'lit/decorators.js'
import { Events } from '../../events/Events.js'
import AppNavigation from '../navigation/internals/AppNavigationElement.js'
import { HttpProject } from '@api-client/core/models/HttpProject.js'
import { ProjectFolderKind } from '@api-client/core/models/kinds.js'
import { ProjectFolder } from '@api-client/core/models/ProjectFolder.js'
import { ProjectRequest, Kind as ProjectRequestKind } from '@api-client/core/models/ProjectRequest.js'
import { Environment, Kind as EnvironmentKind } from '@api-client/core/models/Environment.js'

export default class ProjectNavigation extends AppNavigation {
  /**
   * The instance of the project to create the navigation for.
   */
  @property({ type: Object }) accessor project: HttpProject | undefined

  /**
   * Computed value. Returns true when the project has any items to render in the view.
   */
  get hasItems(): boolean {
    const { project } = this
    if (!project) {
      return false
    }
    // the HttpProject doesn't do any computations when folder is not set. It's just a copy of the source array.
    const folders = project.listFolders()
    const requests = project.listRequests()
    const environments = project.listEnvironments()
    return !!folders.length || !!requests.length || !!environments.length
  }

  constructor() {
    super()
    this.noVisualSelection = true
  }

  override connectedCallback(): void {
    super.connectedCallback()
    this.setAttribute('aria-label', 'HTTP project')
  }

  protected override willUpdate(cp: PropertyValues<this>): void {
    if (cp.has('project') && this.project) {
      this.label = this.project.info.renderLabel
    }
    super.willUpdate(cp)
  }

  protected override setupDataTransfer(item: HTMLLIElement, dt: DataTransfer): void {
    const { project } = this
    if (!project) {
      return
    }
    super.setupDataTransfer(item, dt)
    dt.setData('text/httpproject', project.key)
  }

  protected override commitName(key: string, kind: string, name: string): void {
    const { project } = this
    if (!project) {
      return
    }
    if (!name) {
      return
    }
    if (kind === ProjectFolderKind) {
      const object = project.findFolder(key)
      if (!object) {
        throw new Error(`Invalid state. Folder not found.`)
      }
      object.info.name = name
    } else if (kind === ProjectRequestKind) {
      const object = project.findRequest(key)
      if (!object) {
        throw new Error(`Invalid state. Request not found.`)
      }
      object.info.name = name
    } else if (kind === EnvironmentKind) {
      const object = project.findEnvironment(key)
      if (!object) {
        throw new Error(`Invalid state. Environment not found.`)
      }
      object.info.name = name
    } else {
      throw new Error(`Invalid state. Unknown kind.`)
    }
    this.edited = undefined
    Events.HttpProject.changed(this)
    Events.HttpProject.State.nameChanged(key, kind, this)
  }

  override render(): TemplateResult | string {
    const { project } = this
    if (!project) {
      return html``
    }
    if (!this.hasItems) {
      return this._emptyStateTemplate()
    }
    const content = this._renderParentChildrenTemplate(project)
    return html` ${this.renderLabel()} ${this.renderOuterList(content)} `
  }

  protected _renderParentChildrenTemplate(parent: HttpProject | ProjectFolder): TemplateResult | string {
    const { key } = parent
    const folders = parent.listFolders()
    const requests = parent.listRequests()
    const environments = parent.listEnvironments()
    const isProject = parent.getProject() === undefined
    const isEmpty = !folders.length && !environments.length && !requests.length
    if (isEmpty) {
      return html`<p class="list-item-content empty" aria-disabled="true">Empty folder</p>`
    }
    return html`
      ${folders.map((f) => this.renderFolder(f, isProject ? undefined : key))}
      ${environments.map((r) => this.renderEnvironment(r, isProject ? undefined : key))}
      ${requests.map((r) => this.renderRequest(r, isProject ? undefined : key))}
    `
  }

  protected _emptyStateTemplate(): TemplateResult | string {
    return html`
      ${this.renderLabel()}
      <div class="empty-state">
        <p>This project has no items.</p>
        <p>Right-click on this panel to add a request.</p>
      </div>
    `
  }

  protected renderFolder(folder: ProjectFolder, parentKey?: string): TemplateResult | string {
    const content = this._renderParentChildrenTemplate(folder)
    const name = folder.info.name || 'Unnamed folder'
    const { kind, key } = folder
    return this.renderParentListItem(key, kind, name, content, {
      parent: parentKey,
      parentIcon: 'folderFilled',
    })
  }

  protected renderRequest(request: ProjectRequest, parentKey?: string): TemplateResult | string {
    const name = request.info.name || 'Unnamed request'
    const { key, kind } = request
    const content = this.renderItemContent('request', name)
    return this.renderListItem(key, kind, name, content, {
      parent: parentKey,
      draggable: true,
    })
  }

  protected renderEnvironment(environment: Environment, parentKey?: string): TemplateResult | string {
    const name = environment.info.name || 'Unnamed environment'
    const { key, kind } = environment
    const content = this.renderItemContent('environment', name)
    return this.renderListItem(key, kind, name, content, {
      parent: parentKey,
      draggable: true,
    })
  }
}
