import { LitElement, TemplateResult, html, PropertyValues } from 'lit'
import { property, state } from 'lit/decorators.js'
import { unsafeHTML } from 'lit/directives/unsafe-html.js'
import type { IHttpRequest } from '@api-client/core/models/HttpRequest.js'
import CodeSnippet from '../../code-snippets/CodeSnippet.js'
import CurlSnippet from '../../code-snippets/Bash/CurlSnippet.js'
import CcurlSnippet from '../../code-snippets/C/CcurlSnippet.js'
import RawSnippet from '../../code-snippets/HTTP/RawSnippet.js'
import FetchSnippet from '../../code-snippets/JavaScript/FetchSnippet.js'
import AsyncFetchSnippet from '../../code-snippets/JavaScript/AsyncFetchSnippet.js'
import NodeSnippet from '../../code-snippets/JavaScript/NodeSnippet.js'
import XhrSnippet from '../../code-snippets/JavaScript/XhrSnippet.js'
import JQuerySnippet from '../../code-snippets/JavaScript/jQuerySnippet.js'
import AxiosSnippet from '../../code-snippets/JavaScript/AxiosSnippet.js'
import Python27Snippet from '../../code-snippets/Python/Python27Snippet.js'
import Python31Snippet from '../../code-snippets/Python/Python31Snippet.js'
import RequestsSnippet from '../../code-snippets/Python/RequestsSnippet.js'
import PlatformSnippet from '../../code-snippets/Java/PlatformSnippet.js'
import SpringSnippet from '../../code-snippets/Java/SpringSnippet.js'
import DartSnippet from '../../code-snippets/Dart/DartSnippet.js'
import '../../md/dropdown-list/ui-dropdown-list.js'
import '../../md/list/ui-list.js'
import '../../md/list/ui-list-item.js'
import '../../md/button/ui-button.js'
import '../../md/icons/ui-icon.js'

interface CodeGroup {
  label: string
  parent?: string
  snippet?: CodeSnippet
  id: string
}

const commands: CodeGroup[] = [
  {
    id: 'bash-curl',
    label: 'cURL',
    snippet: new CurlSnippet(),
  },
  {
    id: 'http-raw',
    label: 'HTTP',
    snippet: new RawSnippet(),
  },
  {
    id: 'javascript',
    label: 'JavaScript',
  },
  {
    label: 'Async',
    id: 'javascript-async',
    parent: 'javascript',
    snippet: new AsyncFetchSnippet(),
  },
  {
    label: 'Fetch',
    id: 'javascript-fetch',
    parent: 'javascript',
    snippet: new FetchSnippet(),
  },
  {
    label: 'Node',
    id: 'javascript-node',
    parent: 'javascript',
    snippet: new NodeSnippet(),
  },
  {
    label: 'XHR',
    id: 'javascript-xhr',
    parent: 'javascript',
    snippet: new XhrSnippet(),
  },
  {
    label: 'jQuery',
    id: 'javascript-jquery',
    parent: 'javascript',
    snippet: new JQuerySnippet(),
  },
  {
    label: 'Axios',
    id: 'javascript-axios',
    parent: 'javascript',
    snippet: new AxiosSnippet(),
  },
  {
    label: 'Python',
    id: 'python',
  },
  {
    label: 'Requests',
    id: 'python-requests',
    parent: 'python',
    snippet: new RequestsSnippet(),
  },
  {
    label: 'Python 2.7',
    id: 'python-2.7',
    parent: 'python',
    snippet: new Python27Snippet(),
  },
  {
    label: 'Python 3.1',
    id: 'python-3.1',
    parent: 'python',
    snippet: new Python31Snippet(),
  },
  {
    label: 'C',
    id: 'c-curl',
    snippet: new CcurlSnippet(),
  },
  {
    label: 'Java',
    id: 'java',
  },
  {
    label: 'Spring',
    id: 'java-spring',
    parent: 'java',
    snippet: new SpringSnippet(),
  },
  {
    label: 'Native',
    id: 'java-platform',
    parent: 'java',
    snippet: new PlatformSnippet(),
  },
  {
    label: 'Dart',
    id: 'dart',
    snippet: new DartSnippet(),
  },
]

const topTabs = commands.filter((i) => !i.parent)

/**
 * An element that renders various code snippets from a basic definition of an HTTP request.
 */
export default class HttpSnippets extends LitElement {
  /**
   * The currently selected snippet.
   * @attribute
   */
  @property({ type: String }) accessor selected = 'bash-curl'

  protected parentLanguage = 'bash-curl'

  @property({ type: Object }) accessor request: IHttpRequest | undefined

  @state() protected accessor code: string | undefined

  /**
   * The list of tab names to render in the view.
   */
  protected get groupTabs(): CodeGroup[] {
    const result: CodeGroup[] = []
    commands.forEach((i) => {
      if (i.parent === this.parentLanguage) {
        result.push(i)
      }
    })
    return result
  }

  protected override update(cp: PropertyValues<this>): void {
    if (cp.has('request') || cp.has('selected')) {
      this.triggerCommand()
    }
    super.update(cp)
  }

  protected triggerCommand(): void {
    const { selected = 'bash-curl', request } = this
    if (!request) {
      return
    }
    const instance = commands.find((i) => i.id === selected)
    if (!instance || !instance.snippet) {
      return
    }
    this.code = instance.snippet.tokenize(request)
  }

  protected handleGroupSelect(e: CustomEvent): void {
    const target = e.detail.item as HTMLElement | undefined
    if (!target) {
      return
    }
    const { id } = target.dataset
    if (!id) {
      return
    }
    if (this.parentLanguage === id) {
      return
    }
    const item = commands.find((i) => i.id === id)
    if (!item) {
      return
    }
    if (!item.snippet) {
      const firstChild = commands.find((i) => i.parent === id) as CodeGroup
      this.selected = firstChild.id
      this.parentLanguage = id
    } else {
      this.selected = id
      this.parentLanguage = id
    }
  }

  protected handleSubGroup(e: CustomEvent): void {
    const target = e.detail.item as HTMLElement | undefined
    if (!target) {
      return
    }
    const { id } = target.dataset
    if (!id) {
      return
    }
    this.selected = id
  }

  protected override render(): TemplateResult {
    return html` ${this.renderSelectionOptions()} ${this.renderCode()} `
  }

  protected renderSelectionOptions(): TemplateResult {
    return html` <div class="selection-options">${this.renderLanguageOptions()} ${this.renderFrameworkOptions()}</div> `
  }

  protected renderLanguageOptions(): TemplateResult {
    const selectedKey = this.parentLanguage
    const selectedItem = topTabs.find((i) => i.id === selectedKey)
    const label = selectedItem ? selectedItem.label : 'Language'

    return html`
      <ui-dropdown-list
        verticalAlign="top"
        class="group-dropdown"
        closeOnOutsideClick
        @select="${this.handleGroupSelect}"
      >
        <ui-button color="tonal" title="Select language" aria-label="Opens a menu to select the language">
          <ui-icon icon="arrowDropDown" slot="icon" role="presentation"></ui-icon>
          ${label}
        </ui-button>
        <ui-list slot="dropdown" role="menu">
          ${topTabs.map((tab) => html`<ui-list-item role="menuitem" data-id="${tab.id}">${tab.label}</ui-list-item>`)}
        </ui-list>
      </ui-dropdown-list>
    `
  }

  protected renderFrameworkOptions(): TemplateResult | string {
    const { groupTabs, selected } = this
    if (!groupTabs.length) {
      return ''
    }
    const selectedItem = groupTabs.find((i) => i.id === selected)
    const label = selectedItem ? selectedItem.label : 'Framework'
    return html`
      <ui-dropdown-list verticalAlign="top" class="group-dropdown" closeOnOutsideClick @select="${this.handleSubGroup}">
        <ui-button color="tonal" title="Select a framework" aria-label="Opens a menu to select the framework">
          <ui-icon icon="arrowDropDown" slot="icon" role="presentation"></ui-icon>
          ${label}
        </ui-button>
        <ui-list slot="dropdown" role="menu">
          ${groupTabs.map((tab) => html`<ui-list-item role="menuitem" data-id="${tab.id}">${tab.label}</ui-list-item>`)}
        </ui-list>
      </ui-dropdown-list>
    `
  }

  protected renderCode(): TemplateResult | string {
    if (!this.code) {
      return ''
    }
    return html` <code class="code language-snippet"><pre>${unsafeHTML(this.code)}</pre></code> `
  }
}
