/*
 *  ModelMenu.tsx
 *  Zyquo Cloud Web
 *
 *  Author: Simon-Pierre Boucher
 *  Mail: contact@spboucher.ai
 *
 *  The Model Menu — command-palette-style picker (also opened with ⌘K via the
 *  command palette's model section). Instant fuzzy search, capability filter
 *  chips, Favorites + Recents groups, provider sections (keyed providers
 *  first), capability/context/price badges, per-row star, "not configured"
 *  affordance, keyboard navigation.
 */

import { useEffect, useMemo, useRef, useState } from 'react'
import { CATALOG, priceBadge } from '../features/catalogHelpers'
import { PROVIDER_META } from '../providers/registry'
import { useStore } from '../state/store'
import { contextBadge, type AIModel, type Provider } from '../types'
import {
  IconBrain,
  IconEye,
  IconSearch,
  IconStar,
  IconWrench,
  ProviderGlyph,
} from './icons'

const FILTERS = [
  { key: 'vision', label: 'Vision' },
  { key: 'tools', label: 'Tools' },
  { key: 'reasoning', label: 'Reasoning' },
  { key: 'jsonMode', label: 'JSON' },
  { key: 'long', label: 'Long context' },
  { key: 'cheap', label: 'Cheapest' },
] as const

type FilterKey = (typeof FILTERS)[number]['key']

/** Simple subsequence fuzzy match; returns a score (lower = better) or null. */
function fuzzyScore(query: string, target: string): number | null {
  const q = query.toLowerCase()
  const t = target.toLowerCase()
  if (q === '') return 0
  const direct = t.indexOf(q)
  if (direct !== -1) return direct
  let qi = 0
  let gaps = 0
  let last = -1
  for (let ti = 0; ti < t.length && qi < q.length; ti++) {
    if (t[ti] === q[qi]) {
      if (last !== -1) gaps += ti - last - 1
      last = ti
      qi++
    }
  }
  return qi === q.length ? 100 + gaps : null
}

interface Row {
  model: AIModel
  section: string
}

export default function ModelMenu({
  onPick,
  onClose,
  currentID,
  title = 'Switch model',
}: {
  onPick: (model: AIModel, scope: 'conversation' | 'default' | 'message') => void
  onClose: () => void
  currentID?: string
  title?: string
}) {
  const settings = useStore((s) => s.settings)
  const updateSettings = useStore((s) => s.updateSettings)
  const keyedProviders = useStore((s) => s.keyedProviders)
  const openSettings = useStore((s) => s.openSettings)
  const [query, setQuery] = useState('')
  const [filters, setFilters] = useState<Set<FilterKey>>(new Set())
  const [highlight, setHighlight] = useState(0)
  const inputRef = useRef<HTMLInputElement>(null)
  const listRef = useRef<HTMLDivElement>(null)

  useEffect(() => inputRef.current?.focus(), [])

  const favoriteSet = useMemo(() => new Set(settings.favoriteModelIDs), [settings.favoriteModelIDs])

  const rows: Row[] = useMemo(() => {
    let models = [...CATALOG]
    // Capability filter chips
    if (filters.has('vision')) models = models.filter((m) => m.capabilities.vision)
    if (filters.has('tools')) models = models.filter((m) => m.capabilities.tools)
    if (filters.has('reasoning')) models = models.filter((m) => m.capabilities.reasoning)
    if (filters.has('jsonMode')) models = models.filter((m) => m.capabilities.jsonMode)
    if (filters.has('long')) models = models.filter((m) => m.contextWindow >= 400_000)
    if (filters.has('cheap')) {
      models = models
        .filter((m) => m.pricing)
        .sort((a, b) => (a.pricing?.outputPerMTok ?? 0) - (b.pricing?.outputPerMTok ?? 0))
        .slice(0, 20)
    }
    // Fuzzy search over name + id + provider
    if (query !== '') {
      const scored = models
        .map((m) => {
          const scores = [
            fuzzyScore(query, m.displayName),
            fuzzyScore(query, m.id),
            fuzzyScore(query, PROVIDER_META[m.provider].displayName),
          ].filter((s): s is number => s !== null)
          return scores.length > 0 ? { m, score: Math.min(...scores) } : null
        })
        .filter((x): x is { m: AIModel; score: number } => x !== null)
        .sort((a, b) => a.score - b.score)
      return scored.map(({ m }) => ({ model: m, section: 'Results' }))
    }
    // Grouped: aliases → favorites → recents → providers (keyed first)
    const out: Row[] = []
    for (const [alias, ref] of Object.entries(settings.aliases)) {
      const model = models.find((m) => m.provider === ref.provider && m.id === ref.modelID)
      if (model) out.push({ model, section: `Alias: ${alias}` })
    }
    const favorites = models.filter((m) => favoriteSet.has(m.id))
    for (const model of favorites) out.push({ model, section: 'Favorites' })
    const recents = settings.recentModelIDs
      .map((id) => models.find((m) => m.id === id))
      .filter((m): m is AIModel => m !== undefined && !favoriteSet.has(m.id))
      .slice(0, 5)
    for (const model of recents) out.push({ model, section: 'Recents' })
    const providerOrder: Provider[] = [...new Set(models.map((m) => m.provider))].sort((a, b) => {
      const ak = keyedProviders.includes(a) ? 0 : 1
      const bk = keyedProviders.includes(b) ? 0 : 1
      if (ak !== bk) return ak - bk
      return PROVIDER_META[a].displayName.localeCompare(PROVIDER_META[b].displayName)
    })
    for (const provider of providerOrder) {
      const list = models
        .filter((m) => m.provider === provider)
        .sort((a, b) => rankOf(a) - rankOf(b))
      for (const model of list) {
        out.push({ model, section: PROVIDER_META[provider].displayName })
      }
    }
    return out

    function rankOf(m: AIModel): number {
      if (favoriteSet.has(m.id)) return 0
      if (m.isRecommended) return 1
      if (m.isLegacy) return 3
      return 2
    }
  }, [query, filters, settings.aliases, settings.recentModelIDs, favoriteSet, keyedProviders])

  useEffect(() => setHighlight(0), [query, filters])

  const pick = (model: AIModel, scope: 'conversation' | 'default' | 'message' = 'conversation') => {
    updateSettings({
      recentModelIDs: [model.id, ...settings.recentModelIDs.filter((id) => id !== model.id)].slice(
        0,
        8
      ),
    })
    onPick(model, scope)
    onClose()
  }

  const onKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault()
      setHighlight((h) => Math.min(h + 1, rows.length - 1))
      scrollToHighlight(1)
    } else if (e.key === 'ArrowUp') {
      e.preventDefault()
      setHighlight((h) => Math.max(h - 1, 0))
      scrollToHighlight(-1)
    } else if (e.key === 'Enter') {
      e.preventDefault()
      const row = rows[highlight]
      if (row) pick(row.model, e.metaKey || e.ctrlKey ? 'default' : 'conversation')
    } else if (e.key === 'Escape') {
      onClose()
    }
  }

  const scrollToHighlight = (dir: number) => {
    requestAnimationFrame(() => {
      const el = listRef.current?.querySelector('.model-row.highlighted')
      el?.scrollIntoView({ block: dir > 0 ? 'nearest' : 'nearest' })
    })
  }

  const toggleFavorite = (e: React.MouseEvent, model: AIModel) => {
    e.stopPropagation()
    const next = favoriteSet.has(model.id)
      ? settings.favoriteModelIDs.filter((id) => id !== model.id)
      : [...settings.favoriteModelIDs, model.id]
    updateSettings({ favoriteModelIDs: next })
  }

  let lastSection = ''
  let rowIndex = -1

  return (
    <div className="overlay" onMouseDown={(e) => e.target === e.currentTarget && onClose()}>
      <div className="panel" role="dialog" aria-label={title} onKeyDown={onKeyDown}>
        <div className="panel-search">
          <IconSearch size={14} />
          <input
            ref={inputRef}
            placeholder="Search models, providers, capabilities…"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
          />
        </div>
        <div className="panel-filters">
          {FILTERS.map((f) => (
            <button
              key={f.key}
              className={`filter-chip${filters.has(f.key) ? ' active' : ''}`}
              onClick={() =>
                setFilters((prev) => {
                  const next = new Set(prev)
                  if (next.has(f.key)) next.delete(f.key)
                  else next.add(f.key)
                  return next
                })
              }
            >
              {f.label}
            </button>
          ))}
        </div>
        <div className="panel-list" ref={listRef}>
          {rows.length === 0 && <div className="panel-empty">No models match.</div>}
          {rows.map((row) => {
            rowIndex++
            const index = rowIndex
            const header =
              row.section !== lastSection ? (
                <div className="panel-section-header" key={`h-${row.section}`}>
                  {row.section}
                </div>
              ) : null
            lastSection = row.section
            const model = row.model
            const keyed = keyedProviders.includes(model.provider)
            return (
              <div key={`${row.section}-${model.provider}-${model.id}`}>
                {header}
                <button
                  className={`model-row${index === highlight ? ' highlighted' : ''}${
                    model.id === currentID ? ' current' : ''
                  }`}
                  onClick={(e) =>
                    pick(model, e.metaKey || e.ctrlKey ? 'default' : 'conversation')
                  }
                  onMouseMove={() => setHighlight(index)}
                >
                  <span className="glyph">
                    <ProviderGlyph provider={model.provider} />
                  </span>
                  <span className="model-row-name">{model.displayName}</span>
                  <span className="model-row-provider">
                    {PROVIDER_META[model.provider].displayName}
                  </span>
                  {model.isRecommended && <span className="badge accent">Featured</span>}
                  {model.isLegacy && <span className="badge">Legacy</span>}
                  {!keyed && (
                    <button
                      className="badge warn"
                      title="No API key configured — add one"
                      onClick={(e) => {
                        e.stopPropagation()
                        onClose()
                        openSettings('providers')
                      }}
                    >
                      Add key
                    </button>
                  )}
                  <span className="model-row-badges">
                    {model.capabilities.vision && (
                      <span className="capability-badge" title="Vision">
                        <IconEye />
                      </span>
                    )}
                    {model.capabilities.reasoning && (
                      <span className="capability-badge" title="Reasoning">
                        <IconBrain />
                      </span>
                    )}
                    {model.capabilities.tools && (
                      <span className="capability-badge" title="Tools">
                        <IconWrench />
                      </span>
                    )}
                    <span>{contextBadge(model)}</span>
                    <span title="USD per 1M tokens in / out">{priceBadge(model)}</span>
                    <button
                      className={`star${favoriteSet.has(model.id) ? ' active' : ''}`}
                      title="Favorite"
                      onClick={(e) => toggleFavorite(e, model)}
                    >
                      <IconStar filled={favoriteSet.has(model.id)} />
                    </button>
                  </span>
                </button>
              </div>
            )
          })}
        </div>
        <div className="panel-footer">
          <span>
            <kbd>↑↓</kbd> navigate
          </span>
          <span>
            <kbd>↵</kbd> use for this chat
          </span>
          <span>
            <kbd>⌘↵</kbd> set as default
          </span>
          <span>
            <kbd>esc</kbd> close
          </span>
        </div>
      </div>
    </div>
  )
}
