import { html, type TemplateResult, LitElement } from 'lit'
import { state, property } from 'lit/decorators.js'
import {
  TEMPLATE_METADATA_REGISTRY,
  TEMPLATE_CATEGORIES,
  searchTemplates,
  getTemplatesByCategory,
} from '@api-client/core/modeling/templates/meta/index.js'
import type {
  AssociationInfo,
  DomainTemplate,
  EntityInfo,
  ModelInfo,
  NamespaceInfo,
  PropertyInfo,
} from '@api-client/core/modeling/templates/types.js'
import type { IconType } from '../../md/icons/Icons.js'

export const availableCategories = Object.keys(TEMPLATE_CATEGORIES) as (keyof typeof TEMPLATE_CATEGORIES)[]

/**
 * The selection detail object dispatched by the `selectionchange` event.
 */
export interface BrowseTemplatesSelection {
  /**
   * The key of the selected template
   */
  selected: string
}

/**
 * Generates an icon name for the DomainProperty that illustrate its primary purpose.
 */
export function getPropertyIcon(p: PropertyInfo): IconType {
  if (p.primary) {
    return 'key'
  }
  switch (p.type) {
    // case 'integer':
    case 'number':
      return 'number'
    case 'date':
      return 'calendarToday'
    case 'time':
      return 'schedule'
    case 'datetime':
      return 'event'
    case 'boolean':
      return 'checkCircle'
    case 'binary':
      return 'draft'
    default:
      return 'textFields'
  }
}

/**
 * A browser component for exploring and selecting domain templates.
 * It provides the UI for searching, filtering, previewing, and selecting data domain templates.
 *
 * It manages the filtering state internally but allows property binding for query and category.
 *
 * @fires selectionchange - Dispatched when the user selects a template item. The detail object contains the `selected`
 *                          status which is the template ID (e.g. `CustomEvent<{ selected: string }>`).
 */
export default class DomainTemplateBrowser extends LitElement {
  /**
   * The user entered search query to filter the templates.
   * If not set, all templates are shown (unless filtered by category).
   * @attribute
   */
  @property({ type: String }) accessor query: string | undefined

  /**
   * The selected template category to filter the list.
   * If not set, all categories are included.
   * @attribute
   */
  @property({ type: String }) accessor category: keyof typeof TEMPLATE_CATEGORIES | undefined

  /**
   * The internal list of filtered templates to render in the UI.
   * This is computed based on `query` and `category`.
   */
  @state() accessor templates: DomainTemplate[] = [...Object.values(TEMPLATE_METADATA_REGISTRY)]

  /**
   * The currently previewed template. When set, the component renders the preview view instead of the list.
   */
  @state() accessor previewTemplate: DomainTemplate | undefined

  /**
   * Handles keydown events on the search input.
   * Triggers a search on Enter or resets on Escape.
   */
  protected handleSearchKeydown(e: KeyboardEvent): void {
    const input = e.currentTarget as HTMLInputElement
    const value = input.value.trim()
    if (e.key === 'Enter' && value) {
      this.search(value)
    } else if (e.key === 'Escape') {
      e.preventDefault()
      e.stopPropagation()
      input.value = ''
      this.search()
    }
  }

  /**
   * Performs an in-memory search for templates matching the provided query.
   * Updates the `templates` state with the search results.
   *
   * @param value The search query. If empty, resets the search.
   */
  search(value?: string): void {
    this.query = value
    if (!value) {
      this.templates = [...Object.values(TEMPLATE_METADATA_REGISTRY)]
    } else {
      this.templates = searchTemplates(value)
    }
  }

  protected handleFilterClick(e: Event): void {
    const target = e.target as HTMLElement
    const value = target.dataset.value
    // Toggle category selection
    if (this.category === value) {
      return
    }
    if (value === '') {
      this.category = undefined
      this.templates = [...Object.values(TEMPLATE_METADATA_REGISTRY)]
    } else {
      this.category = value as keyof typeof TEMPLATE_CATEGORIES
      this.templates = getTemplatesByCategory(this.category)
    }
  }

  protected handleTemplateSelect(e: Event): void {
    const templateId = (e.currentTarget as HTMLElement).dataset.id
    if (!templateId) {
      return
    }
    // Dispatch selection event
    this.dispatchEvent(
      new CustomEvent<BrowseTemplatesSelection>('selectionchange', {
        detail: { selected: templateId },
        bubbles: false,
      })
    )
  }

  protected handleTemplatePreview(e: Event): void {
    e.stopPropagation()
    const templateId = (e.currentTarget as HTMLElement).dataset.id
    if (!templateId) {
      return
    }
    this.previewTemplate = this.templates.find((t) => t.id === templateId)
  }

  protected handleClosePreview(): void {
    this.previewTemplate = undefined
  }

  protected override render(): TemplateResult {
    if (this.previewTemplate) {
      return this.renderPreviewMode()
    }

    return html`
      <div class="controls">${this.renderTopControls()} ${this.renderCategoryFilters()}</div>
      ${this.templates.length > 0 ? this.renderTemplatesGrid() : this.renderEmptyState()}
    `
  }

  protected renderTopControls(): TemplateResult {
    return html`
      <div class="top-row">
        <div class="search-group">${this.renderSearchInput()}</div>
      </div>
    `
  }

  protected renderSearchInput(): TemplateResult {
    return html`
      <ui-outlined-text-field
        id="template-search"
        label="Search templates..."
        type="search"
        class="search"
        .value="${this.query ?? ''}"
        @keydown="${this.handleSearchKeydown}"
      >
        <ui-icon slot="prefix">search</ui-icon>
      </ui-outlined-text-field>
    `
  }

  protected renderCategoryFilters(): TemplateResult {
    return html`
      <div class="category-tabs">
        <ui-button-group type="connected">
          <ui-button
            color="tonal"
            size="s"
            toggle
            data-value=""
            ?selected="${!this.category}"
            @click="${this.handleFilterClick}"
          >
            All
          </ui-button>
          ${availableCategories.map((category) => {
            const isSelected = this.category === category
            return html`<ui-button
              color="tonal"
              size="s"
              toggle
              data-value="${category}"
              ?selected="${isSelected}"
              @click="${this.handleFilterClick}"
            >
              ${category}
            </ui-button>`
          })}
        </ui-button-group>
      </div>
    `
  }

  protected renderTemplatesGrid(): TemplateResult {
    return html`
      <div class="templates-grid">${this.templates.map((template) => this.renderTemplateCard(template))}</div>
    `
  }

  protected renderTemplateCard(template: DomainTemplate): TemplateResult {
    // Use available properties from DomainTemplate interface
    const templateName = template.name || 'Unnamed Template'
    const templateDescription = template.description || 'No description available'

    return html`
      <div class="template-card">
        <h3 class="template-title display-small">${templateName}</h3>
        <p class="template-description body-medium">${templateDescription}</p>

        <div class="template-actions">
          <ui-button data-id="${template.id}" color="outlined" size="s" @click="${this.handleTemplatePreview}">
            Details
          </ui-button>
          <ui-button color="filled" size="s" data-id="${template.id}" @click="${this.handleTemplateSelect}">
            Use template
          </ui-button>
        </div>
      </div>
    `
  }

  protected renderEmptyState(): TemplateResult {
    const message = this.query
      ? `No templates found for "${this.query}"`
      : this.category
        ? `No templates found in "${this.category}" category`
        : 'No templates available'

    const description =
      this.query || this.category ? 'Try adjusting your search or filters' : 'Templates will appear here when available'

    return html`
      <div class="empty-state">
        <ui-icon class="empty-icon">search_off</ui-icon>
        <h3 class="empty-title title-large">${message}</h3>
        <p class="empty-description body-large">${description}</p>
      </div>
    `
  }

  renderPreviewMode(): TemplateResult {
    return html`
      <div class="preview-header">
        <ui-button color="text" @click="${this.handleClosePreview}">
          <ui-icon slot="icon">arrow_back</ui-icon>
          Back to Templates
        </ui-button>
      </div>
      <div class="preview-main">${this.renderTemplateDetails(this.previewTemplate as DomainTemplate)}</div>
    `
  }

  protected renderTemplateDetails(template: DomainTemplate): TemplateResult {
    return html`
      <div class="template-details">
        <h1 class="template-details-title display-small">${template.name}</h1>
        <div class="template-details-header">
          <div class="template-details-info">
            <p class="template-details-description">${template.description}</p>
            <div class="template-metadata">
              <div class="metadata-item">
                <span class="metadata-label">Author:</span>
                <span class="metadata-value">${template.author}</span>
              </div>
              <div class="metadata-item">
                <span class="metadata-label">Version:</span>
                <span class="metadata-value">${template.version}</span>
              </div>
              <div class="metadata-item">
                <span class="metadata-label">Created:</span>
                <span class="metadata-value">${new Date(template.createdAt).toLocaleDateString()}</span>
              </div>
              <div class="metadata-item">
                <span class="metadata-label">Updated:</span>
                <span class="metadata-value">${new Date(template.updatedAt).toLocaleDateString()}</span>
              </div>
            </div>
            <div class="template-tags">
              ${template.tags.map((tag) => html`<span class="template-tag">${tag}</span>`)}
            </div>
          </div>
          <div class="template-details-actions">
            <ui-button color="filled" size="s" data-id="${template.id}" @click="${this.handleTemplateSelect}">
              <ui-icon slot="icon">download</ui-icon>
              Use This Template
            </ui-button>
          </div>
        </div>

        <div class="template-structure">
          <h2 class="structure-title title-large">Data Domain Structure</h2>
          ${this.renderDomainStructure(template)}
        </div>
      </div>
    `
  }

  protected renderDomainStructure(template: DomainTemplate): TemplateResult {
    const { structure } = template
    const { namespaces } = structure

    return html`<div class="domain-structure">${this.renderNamespaces(namespaces)}</div>`
  }

  protected renderNamespaces(namespaces: NamespaceInfo[]): TemplateResult {
    return html`
      <div class="namespaces-section">
        <h3 class="title-medium">Namespaces</h3>
        ${namespaces.map(
          (namespace) => html`
            <div class="namespace-item">
              <div class="namespace-header">
                <ui-icon class="namespace-icon" icon="schemaNamespace"></ui-icon>
                <h4 class="title-small">${namespace.displayName || namespace.name}</h4>
              </div>
              ${namespace.description
                ? html`<p class="namespace-description body-medium">${namespace.description}</p>`
                : ''}
              ${this.renderModels(namespace.models)}
            </div>
          `
        )}
      </div>
    `
  }

  protected renderModels(models: ModelInfo[]): TemplateResult {
    return html`
      <div class="models-section">
        ${models.map(
          (model) => html`
            <div class="domain-model">
              <div class="domain-model-header">
                <ui-icon class="model-icon" icon="schemaModel"></ui-icon>
                <h5 class="title-small">${model.displayName || model.name}</h5>
              </div>
              ${model.description ? html`<p class="model-description body-medium">${model.description}</p>` : ''}
              <div class="model-entities">${model.entities.map((entity) => this.renderDomainEntity(entity))}</div>
            </div>
          `
        )}
      </div>
    `
  }

  protected renderDomainEntity(entity: EntityInfo): TemplateResult {
    return html`
      <div class="domain-entity">
        <div class="entity-header">
          <div class="entity-title">
            <ui-icon class="entity-icon" icon="schemaEntity"></ui-icon>
            <h6 class="entity-name label-large">${entity.displayName || entity.name}</h6>
            ${entity.deprecated ? html`<span class="deprecated-badge">Deprecated</span>` : ''}
          </div>
          ${entity.description ? html`<p class="entity-description body-medium">${entity.description}</p>` : ''}
        </div>
        ${entity.semantics?.length > 0
          ? html`
              <div class="entity-semantics">
                <span class="semantics-label">Semantics:</span>
                ${entity.semantics.map((semantic: string) => html`<span class="semantic-tag">${semantic}</span>`)}
              </div>
            `
          : ''}
        ${entity.properties?.length > 0
          ? html`
              <div class="entity-section">
                <h5 class="section-title title-small">Properties (${entity.propertyCount})</h5>
                <div class="properties-list">${entity.properties.map((prop) => this.renderProperty(prop))}</div>
              </div>
            `
          : ''}
        ${entity.associations?.length > 0
          ? html`
              <div class="entity-section">
                <h5 class="section-title title-small">Associations (${entity.associationCount})</h5>
                <div class="associations-list">
                  ${entity.associations.map((assoc) => this.renderAssociation(assoc))}
                </div>
              </div>
            `
          : ''}
      </div>
    `
  }

  protected renderProperty(property: PropertyInfo): TemplateResult {
    const badges: string[] = []

    if (property.required) badges.push('Required')
    if (property.primary) badges.push('Primary')
    if (property.unique) badges.push('Unique')
    if (property.readOnly) badges.push('Read-only')
    if (property.writeOnly) badges.push('Write-only')
    if (property.multiple) badges.push('Multiple')
    if (property.deprecated) badges.push('Deprecated')

    const icon = getPropertyIcon(property)
    return html`
      <div class="property-item ${property.deprecated ? 'deprecated' : ''}">
        <div class="property-header">
          <ui-icon class="property-icon" .icon=${icon}></ui-icon>
          <span class="label-large">${property.displayName || property.name}</span>
          <span class="property-type body-small">${property.type}</span>
        </div>
        ${property.description ? html`<p class="property-description body-medium">${property.description}</p>` : ''}
        ${badges.length > 0
          ? html`<div class="property-badges">
              ${badges.map((badge) => html`<span class="property-badge body-small">${badge}</span>`)}
            </div>`
          : ''}
        ${property.defaultValue
          ? html`<div class="property-default">Default: <code>${property.defaultValue}</code></div>`
          : ''}
        ${property.enumValues && property.enumValues.length > 0
          ? html`<div class="property-enum">
              Values: ${property.enumValues.map((val) => html`<code>${val}</code>`)}
            </div>`
          : ''}
        ${property.semantics?.length > 0
          ? html`
              <div class="property-semantics">
                ${property.semantics.map((semantic) => html`<span class="semantic-tag">${semantic}</span>`)}
              </div>
            `
          : ''}
      </div>
    `
  }

  protected renderAssociation(association: AssociationInfo): TemplateResult {
    const badges = []

    if (association.required) badges.push('Required')
    if (association.multiple) badges.push('Multiple')
    if (association.readOnly) badges.push('Read-only')

    return html`
      <div class="association-item">
        <div class="association-header">
          <ui-icon class="association-icon">link</ui-icon>
          <span class="label-large">${association.displayName || association.name}</span>
          <span class="association-targets">${association.targetEntities.join(', ')}</span>
        </div>
        ${association.description ? html`<p class="association-description">${association.description}</p>` : ''}
        ${badges.length > 0
          ? html`<div class="association-badges">
              ${badges.map((badge: string) => html`<span class="association-badge">${badge}</span>`)}
            </div>`
          : ''}
        ${association.semantics?.length > 0
          ? html`
              <div class="association-semantics">
                ${association.semantics.map((semantic: string) => html`<span class="semantic-tag">${semantic}</span>`)}
              </div>
            `
          : ''}
      </div>
    `
  }
}
