import { useCallback, useEffect, useState } from "react"
import { Link } from "react-router"
import {
  Bot,
  Palette,
  User as UserIcon,
  Info,
  Users,
  Building2,
  Plus,
  Trash2,
  ExternalLink,
} from "lucide-react"
import { listModels } from "@crema/llm-ui"
import {
  buildAdapter,
  LLMProvidersSettingsCard,
  resetSettings as resetProviderSettings,
  useSettings as useProviderSettings,
  type LLMProvidersSettings,
} from "@crema/llm-providers-ui"
import { useArcadiaClient } from "@crema/arcadia-core-client"

import { probeProxy, type LLMProxyProvider } from "~/lib/arcadia/llm-proxy"
import { LlmConfigurationsPanel } from "~/components/settings/llm-configurations-panel"
import { AppShell } from "~/components/layout/app-shell"
import { DataState } from "~/components/data-state"
import { ThemeToggle } from "~/components/layout/theme-toggle"
import { BackgroundPicker } from "~/components/layout/background-picker"
import { FontSizePicker } from "~/components/layout/font-size-picker"
import { SurfacePicker } from "~/components/layout/surface-picker"
import { BrandingTab } from "~/components/tenant-detail/branding-tab"
import { LocalizationTab } from "~/components/tenant-detail/localization-tab"
import { getTenant, type Tenant } from "~/lib/arcadia/tenants"
import { useCapabilities } from "~/lib/capabilities"
import { useSession } from "~/lib/session"
import { Button } from "~/components/ui/button"
import { Input } from "~/components/ui/input"
import { Textarea } from "~/components/ui/textarea"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "~/components/ui/card"
import {
  loadActiveAgentId,
  newAgentId,
  resetAgents,
  saveActiveAgentId,
  saveAgents,
  useAgents,
  type Agent,
} from "~/lib/agents"
import { pageTitle } from "~/lib/page-meta"

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

const SECTION_KEY = "crema.settings.section"

type SectionId =
  | "assistant"
  | "personas"
  | "tenant"
  | "appearance"
  | "account"
  | "about"

type SectionDef = {
  id: SectionId
  label: string
  icon: React.ComponentType<{ className?: string }>
  description: string
  /** When set, the section only shows if the session holds this capability. */
  requiresCapability?: string
}

const sections: SectionDef[] = [
  {
    id: "assistant",
    label: "Assistant",
    icon: Bot,
    description: "Model, endpoint & budgets",
  },
  {
    id: "personas",
    label: "Personas",
    icon: Users,
    description: "Assistant personas (this browser)",
  },
  {
    id: "tenant",
    label: "Tenant",
    icon: Building2,
    description: "Branding & localization",
    requiresCapability: "platform.tenants",
  },
  {
    id: "appearance",
    label: "Appearance",
    icon: Palette,
    description: "Theme, font size, surface, background",
  },
  { id: "account", label: "Account", icon: UserIcon, description: "Your profile" },
  { id: "about", label: "About", icon: Info, description: "Version & credits" },
]

export default function SettingsRoute() {
  const arcadia = useArcadiaClient()

  const testConnection = async (
    s: LLMProvidersSettings,
  ): Promise<{ ok: boolean; message: string }> => {
    try {
      const arcadiaBaseURL =
        (import.meta.env.VITE_ARCADIA_URL as string | undefined) ?? "http://localhost:4000"
      const arcadiaTenantId =
        (import.meta.env.VITE_ARCADIA_TENANT as string | undefined) ?? "default"
      const arcadiaAuthToken =
        typeof window !== "undefined"
          ? sessionStorage.getItem("arcadia_access_token") ?? undefined
          : undefined

      const adapter = await buildAdapter({
        settings: s,
        // Direct-mode resolver — fetches the API key from the vault.
        resolveSecret: async (name) => {
          const res = await arcadia.GET<{ data: { value: string } }>(
            `/api/v1/secrets/${encodeURIComponent(name)}`,
          )
          return res.data.value
        },
        // Proxy-mode coordinates.
        arcadiaBaseURL,
        arcadiaAuthToken,
        arcadiaTenantId,
      })

      // Proxy mode: round-trip a 1-token chat to verify auth → secret
      // resolution → upstream dispatch end-to-end. Maps the contract's
      // specific error codes to user-facing messages.
      if (s.mode === "proxy") {
        return probeProxy(arcadia, {
          provider: s.providerId as LLMProxyProvider,
          model: s.model || (s.providerId === "anthropic" ? "claude-opus-4-7" : "gpt-4o-mini"),
          secretName: s.secretName || undefined,
        })
      }

      // Direct mode — for OpenAI-compatible endpoints, /models is a cheap probe.
      if (s.providerId !== "anthropic") {
        const baseURL =
          s.baseURL ||
          (s.providerId === "lmstudio"
            ? "http://localhost:1234/v1"
            : s.providerId === "openai"
              ? "https://api.openai.com/v1"
              : s.providerId === "deepseek"
                ? "https://api.deepseek.com"
                : "https://dashscope-intl.aliyuncs.com/compatible-mode/v1")
        // Resolve key for the probe (lmstudio doesn't need one).
        let apiKey: string | undefined
        if (s.providerId !== "lmstudio" && s.secretName) {
          try {
            const res = await arcadia.GET<{ data: { value: string } }>(
              `/api/v1/secrets/${encodeURIComponent(s.secretName)}`,
            )
            apiKey = res.data.value
          } catch (err) {
            const msg = err instanceof Error ? err.message : String(err)
            if (/404|not[_ ]found/i.test(msg)) {
              return {
                ok: false,
                message: `No vault secret named "${s.secretName}". Create it under /secrets first (paste the API key as the Value), then enter the secret's name here.`,
              }
            }
            throw err
          }
        }
        const ac = new AbortController()
        const t = setTimeout(() => ac.abort(), 5000)
        try {
          const rows = await listModels({ baseURL, apiKey, signal: ac.signal })
          return { ok: true, message: `Connected. ${rows.length} model(s) reachable.` }
        } finally {
          clearTimeout(t)
        }
      }
      // Anthropic doesn't expose a /models list; we just confirm adapter built.
      return { ok: true, message: `Adapter ready (${adapter.label ?? adapter.id}).` }
    } catch (e) {
      return { ok: false, message: e instanceof Error ? e.message : String(e) }
    }
  }

  const caps = useCapabilities()
  const visibleSections = sections.filter(
    (s) => !s.requiresCapability || caps.has(s.requiresCapability as never),
  )

  const [section, setSection] = useState<SectionId>(() => {
    if (typeof window === "undefined") return "assistant"
    const stored = localStorage.getItem(SECTION_KEY)
    return sections.some((s) => s.id === stored)
      ? (stored as SectionId)
      : "assistant"
  })
  // If the stored section isn't available to this user (e.g. Tenant for a
  // non-platform admin), fall back to Assistant.
  useEffect(() => {
    if (!visibleSections.some((s) => s.id === section)) setSection("assistant")
  }, [visibleSections, section])
  useEffect(() => {
    if (typeof window !== "undefined")
      localStorage.setItem(SECTION_KEY, section)
  }, [section])

  return (
    <AppShell>
      <div className="grid gap-6 pt-10 md:grid-cols-[14rem_1fr] md:pt-0">
        <nav
          aria-label="Settings sections"
          className="flex flex-row flex-wrap gap-1 md:flex-col md:flex-nowrap md:gap-0.5"
        >
          {visibleSections.map((s) => {
            const Icon = s.icon
            const active = section === s.id
            return (
              <button
                key={s.id}
                type="button"
                data-action={`settings-section-${s.id}`}
                onClick={() => setSection(s.id)}
                aria-current={active ? "page" : undefined}
                className={[
                  "group flex shrink-0 items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm transition-colors duration-fast ease-standard",
                  active
                    ? "bg-primary/10 text-primary"
                    : "text-muted-foreground hover:bg-accent hover:text-accent-foreground",
                ].join(" ")}
              >
                <Icon className="size-4 shrink-0" />
                <span className="flex flex-col">
                  <span className="font-medium leading-tight">{s.label}</span>
                  <span
                    className={[
                      "hidden text-xs leading-tight md:inline",
                      active ? "text-primary/80" : "text-muted-foreground/80",
                    ].join(" ")}
                  >
                    {s.description}
                  </span>
                </span>
              </button>
            )
          })}
        </nav>

        <div className="min-w-0">
          {section === "assistant" && (
            <div className="flex flex-col gap-4">
              <LlmConfigurationsPanel />

              <details className="rounded-md border bg-muted/20 px-3 py-2 text-sm">
                <summary className="cursor-pointer text-muted-foreground">
                  Advanced: tweak the active session settings (transport, system prompt,
                  context budget) directly
                </summary>
                <div className="pt-3">
                  <LLMProvidersSettingsCard
                    onTest={testConnection}
                    hideTransportToggle={false}
                  />
                </div>
              </details>

              <div className="flex items-center gap-2">
                <Button
                  variant="outline"
                  onClick={() => resetProviderSettings()}
                  data-action="settings-reset"
                >
                  Reset to defaults
                </Button>
                <span className="text-xs text-muted-foreground">
                  Need to manage stored keys? See <a href="/secrets" className="underline">Secrets</a>.
                </span>
              </div>
            </div>
          )}

          {section === "personas" && <AgentsPanel />}

          {section === "tenant" && <TenantSettingsSection />}

          {section === "appearance" && (
            <Card>
              <CardHeader>
                <CardTitle>Appearance</CardTitle>
                <CardDescription>
                  Theme, density, and background for this browser. These are
                  also in the top-right toolbar; they persist locally.
                </CardDescription>
              </CardHeader>
              <CardContent className="flex flex-col divide-y">
                <AppearanceRow label="Theme" hint="Light or dark.">
                  <ThemeToggle />
                </AppearanceRow>
                <AppearanceRow label="Font size" hint="Base text scale.">
                  <FontSizePicker />
                </AppearanceRow>
                <AppearanceRow label="Surface tint" hint="Panel background tint.">
                  <SurfacePicker />
                </AppearanceRow>
                <AppearanceRow label="Background" hint="Ambient page atmosphere.">
                  <BackgroundPicker />
                </AppearanceRow>
              </CardContent>
            </Card>
          )}

          {section === "account" && <AccountSection />}

          {section === "about" && (
            <Card>
              <CardHeader>
                <CardTitle>About</CardTitle>
                <CardDescription>App version and credits.</CardDescription>
              </CardHeader>
              <CardContent className="space-y-1 text-sm text-muted-foreground">
                <p>Arcadia Admin — operator console for arcadia-core.</p>
                <p>Built on the Crema design system.</p>
              </CardContent>
            </Card>
          )}
        </div>
      </div>
    </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>
  )
}

function AppearanceRow({
  label,
  hint,
  children,
}: {
  label: string
  hint: string
  children: React.ReactNode
}) {
  return (
    <div className="flex items-center justify-between gap-4 py-3 first:pt-0 last:pb-0">
      <div>
        <div className="text-sm font-medium">{label}</div>
        <div className="text-xs text-muted-foreground">{hint}</div>
      </div>
      {children}
    </div>
  )
}

function AccountSection() {
  const session = useSession()
  return (
    <Card>
      <CardHeader>
        <CardTitle>Account</CardTitle>
        <CardDescription>Your identity on this deployment.</CardDescription>
      </CardHeader>
      <CardContent className="flex flex-col gap-4">
        <div className="grid gap-3 sm:grid-cols-2">
          <ReadOnlyFact label="Name" value={session?.name ?? "—"} />
          <ReadOnlyFact label="Email" value={session?.email ?? "—"} />
          <ReadOnlyFact label="Active tenant" value={session?.tenantSlug ?? "—"} mono />
          <ReadOnlyFact
            label="Roles"
            value={session?.roles?.length ? session.roles.join(", ") : "—"}
          />
        </div>
        <div>
          <Button asChild variant="outline" data-action="settings-account-profile">
            <Link to="/profile">
              Edit profile <ExternalLink className="size-4" />
            </Link>
          </Button>
        </div>
      </CardContent>
    </Card>
  )
}

function ReadOnlyFact({
  label,
  value,
  mono,
}: {
  label: string
  value: string
  mono?: boolean
}) {
  return (
    <div className="rounded-lg border bg-card/40 px-3 py-2">
      <div className="text-xs uppercase tracking-wider text-muted-foreground">{label}</div>
      <div className={`mt-0.5 text-sm font-medium ${mono ? "font-mono" : ""}`}>{value}</div>
    </div>
  )
}

/**
 * Tenant settings — reuses the Phase-4 tenant-detail tab components, scoped to
 * the operator's active tenant. The branding/localization endpoints are
 * platform-gated, so this section is only offered to platform admins (see the
 * `requiresCapability` on the section); if the load 403s anyway, DataState
 * surfaces it cleanly.
 */
function TenantSettingsSection() {
  const arcadia = useArcadiaClient()
  const session = useSession()
  const tenantId = session?.tenantId

  const [tenant, setTenant] = useState<Tenant | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<unknown>(null)

  const reload = useCallback(async () => {
    if (!tenantId) {
      setLoading(false)
      return
    }
    setError(null)
    setLoading(true)
    try {
      setTenant(await getTenant(arcadia, tenantId))
    } catch (err) {
      setError(err)
    } finally {
      setLoading(false)
    }
  }, [arcadia, tenantId])

  useEffect(() => {
    reload()
  }, [reload])

  return (
    <div className="flex flex-col gap-4">
      <p className="text-sm text-muted-foreground">
        Branding and localization for your active tenant
        {session?.tenantSlug ? (
          <>
            {" "}
            (<code className="font-mono text-xs">{session.tenantSlug}</code>)
          </>
        ) : null}
        . To manage a different tenant, open it from{" "}
        <Link to="/tenants" className="underline">
          Tenants
        </Link>
        .
      </p>
      <DataState
        loading={loading}
        error={error}
        isEmpty={!tenant}
        onRetry={reload}
        loadingLabel="Loading tenant…"
        empty={
          <div className="rounded-lg border py-8 text-center text-sm text-muted-foreground">
            No active tenant on this session.
          </div>
        }
      >
        {tenant ? (
          <div className="flex flex-col gap-6">
            <BrandingTab tenant={tenant} reload={reload} />
            <LocalizationTab tenant={tenant} reload={reload} />
          </div>
        ) : null}
      </DataState>
    </div>
  )
}

function AgentsPanel() {
  const agents = useAgents()
  const [activeId, setActiveId] = useState<string>(() => loadActiveAgentId())
  const [editingId, setEditingId] = useState<string | null>(null)

  useEffect(() => {
    saveActiveAgentId(activeId)
  }, [activeId])

  const editing = agents.find((a) => a.id === editingId) ?? null

  const update = (next: Agent) => {
    saveAgents(agents.map((a) => (a.id === next.id ? next : a)))
  }
  const remove = (id: string) => {
    if (agents.length <= 1) return
    const next = agents.filter((a) => a.id !== id)
    saveAgents(next)
    if (activeId === id) setActiveId(next[0].id)
    if (editingId === id) setEditingId(null)
  }
  const create = () => {
    const id = newAgentId()
    const draft: Agent = {
      id,
      name: "New persona",
      role: "Specialist",
      prompt: "Describe what this persona is good at and how it should respond.",
    }
    saveAgents([...agents, draft])
    setEditingId(id)
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>Personas</CardTitle>
        <CardDescription>
          Assistant personas with their own sub-system prompts. Switch the
          active one in the chat status bar — the assistant inherits its skills,
          tone, and scope. Stored in this browser only, not synced to your
          account.
        </CardDescription>
      </CardHeader>
      <CardContent className="flex flex-col gap-4">
        <div className="flex flex-wrap items-center gap-2">
          <Button
            data-action="settings-agent-new"
            onClick={create}
            size="sm"
            variant="outline"
          >
            <Plus className="size-4" /> New persona
          </Button>
          <Button
            data-action="settings-agent-reset"
            onClick={() => {
              resetAgents()
              setEditingId(null)
            }}
            size="sm"
            variant="ghost"
          >
            Reset to defaults
          </Button>
        </div>

        <ul className="flex flex-col gap-1.5">
          {agents.map((a) => {
            const isActive = activeId === a.id
            const isEditing = editingId === a.id
            return (
              <li
                key={a.id}
                className={[
                  "rounded-lg border transition-colors",
                  isEditing
                    ? "border-primary/40 bg-primary/5"
                    : "border-border bg-card/40",
                ].join(" ")}
              >
                <div className="flex items-center gap-2 px-3 py-2">
                  <button
                    type="button"
                    data-action={`settings-agent-activate-${a.id}`}
                    onClick={() => setActiveId(a.id)}
                    className={[
                      "size-2.5 shrink-0 rounded-full ring-2 transition-colors",
                      isActive
                        ? "bg-primary ring-primary/30"
                        : "bg-muted ring-transparent hover:ring-foreground/20",
                    ].join(" ")}
                    aria-label={
                      isActive ? `${a.name} (active)` : `Activate ${a.name}`
                    }
                    title={isActive ? "Active" : "Set active"}
                  />
                  <div className="flex min-w-0 flex-1 flex-col">
                    <span className="truncate text-sm font-medium">{a.name}</span>
                    <span className="truncate text-xs text-muted-foreground">
                      {a.role}
                    </span>
                  </div>
                  <Button
                    data-action={`settings-agent-edit-${a.id}`}
                    onClick={() => setEditingId(isEditing ? null : a.id)}
                    size="sm"
                    variant="ghost"
                  >
                    {isEditing ? "Done" : "Edit"}
                  </Button>
                  <Button
                    data-action={`settings-agent-delete-${a.id}`}
                    onClick={() => remove(a.id)}
                    size="icon-sm"
                    variant="ghost"
                    disabled={agents.length <= 1}
                    aria-label="Delete persona"
                    title="Delete persona"
                  >
                    <Trash2 className="size-4" />
                  </Button>
                </div>
                {isEditing && editing && (
                  <div className="flex flex-col gap-3 border-t bg-background/60 px-3 py-3">
                    <Field label="Name">
                      <Input
                        data-action={`settings-agent-name-${a.id}`}
                        value={editing.name}
                        onChange={(e) =>
                          update({ ...editing, name: e.target.value })
                        }
                      />
                    </Field>
                    <Field label="Role">
                      <Input
                        data-action={`settings-agent-role-${a.id}`}
                        value={editing.role}
                        onChange={(e) =>
                          update({ ...editing, role: e.target.value })
                        }
                      />
                    </Field>
                    <Field
                      label="Sub-system prompt"
                      hint="Stacked on top of the main system prompt only when this persona is active."
                    >
                      <Textarea
                        data-action={`settings-agent-prompt-${a.id}`}
                        value={editing.prompt}
                        onChange={(e) =>
                          update({ ...editing, prompt: e.target.value })
                        }
                        rows={6}
                        className="min-h-32 font-mono text-xs"
                      />
                    </Field>
                  </div>
                )}
              </li>
            )
          })}
        </ul>
      </CardContent>
    </Card>
  )
}

export { RouteErrorBoundary as ErrorBoundary } from "~/components/route-error"
