import { useEffect, useState } from "react"
import { Check, X, Loader2 } from "lucide-react"
import { listModels } from "@crema/llm-ui"

import { AppShell } from "~/components/layout/app-shell"
import { Button } from "~/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "~/components/ui/card"
import { Input } from "~/components/ui/input"
import {
  DEFAULT_SETTINGS,
  saveLLMSettings,
  useLLMSettings,
  type LLMSettings,
} from "~/lib/llm-settings"
import { pageTitle } from "~/lib/page-meta"

export const meta = () => pageTitle("Settings")

type TestState =
  | { kind: "idle" }
  | { kind: "running" }
  | { kind: "ok"; count: number }
  | { kind: "fail"; reason: string }

export default function SettingsRoute() {
  const settings = useLLMSettings()
  const [draft, setDraft] = useState<LLMSettings>(settings)
  const [savedAt, setSavedAt] = useState<number | null>(null)
  const [test, setTest] = useState<TestState>({ kind: "idle" })

  useEffect(() => {
    setDraft(settings)
  }, [settings])

  const runTest = async () => {
    setTest({ kind: "running" })
    const ac = new AbortController()
    const timeout = setTimeout(() => ac.abort(), 4000)
    try {
      const rows = await listModels({ baseURL: draft.baseURL, signal: ac.signal })
      setTest({ kind: "ok", count: rows.length })
    } catch (e) {
      setTest({
        kind: "fail",
        reason: e instanceof Error ? e.message : String(e),
      })
    } finally {
      clearTimeout(timeout)
    }
  }

  const dirty =
    draft.baseURL !== settings.baseURL ||
    draft.contextTokens !== settings.contextTokens ||
    draft.responseBudget !== settings.responseBudget

  const save = () => {
    saveLLMSettings(draft)
    setSavedAt(Date.now())
  }

  const reset = () => {
    setDraft(DEFAULT_SETTINGS)
  }

  return (
    <AppShell title="Settings">
      <Card>
        <CardHeader>
          <CardTitle>LLM</CardTitle>
          <CardDescription>
            Configure the local model endpoint and context budgets used by the
            Assistant.
          </CardDescription>
        </CardHeader>
        <CardContent className="flex flex-col gap-5">
          <Field
            label="Base URL"
            hint="OpenAI-compatible endpoint. LM Studio defaults to http://localhost:1234/v1."
          >
            <Input
              data-action="settings-base-url"
              value={draft.baseURL}
              onChange={(e) =>
                setDraft((d) => ({ ...d, baseURL: e.target.value }))
              }
              placeholder="http://localhost:1234/v1"
              spellCheck={false}
              autoComplete="off"
            />
          </Field>

          <Field
            label="Context window (tokens)"
            hint="Match this to the context length you've loaded in LM Studio."
          >
            <Input
              data-action="settings-context-tokens"
              type="number"
              min={1024}
              step={512}
              value={draft.contextTokens}
              onChange={(e) =>
                setDraft((d) => ({
                  ...d,
                  contextTokens: Number(e.target.value) || d.contextTokens,
                }))
              }
            />
          </Field>

          <Field
            label="Response cap (max tokens)"
            hint="Upper bound on each model reply. Smaller = faster, less rambling."
          >
            <Input
              data-action="settings-response-budget"
              type="number"
              min={64}
              step={64}
              value={draft.responseBudget}
              onChange={(e) =>
                setDraft((d) => ({
                  ...d,
                  responseBudget: Number(e.target.value) || d.responseBudget,
                }))
              }
            />
          </Field>

          <div className="flex flex-wrap items-center gap-2">
            <Button
              data-action="settings-save"
              onClick={save}
              disabled={!dirty}
            >
              Save
            </Button>
            <Button
              data-action="settings-test"
              variant="outline"
              onClick={runTest}
              disabled={test.kind === "running"}
            >
              {test.kind === "running" ? (
                <Loader2 className="size-4 animate-spin" />
              ) : test.kind === "ok" ? (
                <Check className="size-4 text-emerald-600" />
              ) : test.kind === "fail" ? (
                <X className="size-4 text-destructive" />
              ) : null}
              Test connection
            </Button>
            <Button
              data-action="settings-reset"
              variant="outline"
              onClick={reset}
            >
              Reset to defaults
            </Button>
            {savedAt && !dirty && (
              <span className="text-sm text-muted-foreground">Saved.</span>
            )}
            {test.kind === "ok" && (
              <span className="text-sm text-emerald-700 dark:text-emerald-400">
                {test.count} model{test.count === 1 ? "" : "s"} available.
              </span>
            )}
            {test.kind === "fail" && (
              <span className="text-sm text-destructive" title={test.reason}>
                Failed: {test.reason.slice(0, 60)}
              </span>
            )}
          </div>
        </CardContent>
      </Card>
    </AppShell>
  )
}

function Field({
  label,
  hint,
  children,
}: {
  label: string
  hint?: string
  children: React.ReactNode
}) {
  return (
    <label className="flex flex-col gap-1.5">
      <span className="text-sm font-medium">{label}</span>
      {children}
      {hint && <span className="text-xs text-muted-foreground">{hint}</span>}
    </label>
  )
}
