import * as React from "react";
import {
  Activity,
  CheckCircle2,
  Cpu,
  FlaskConical,
  Plus,
  Power,
  RefreshCw,
  Save,
  Trash2,
} from "lucide-react";
import {
  createModel,
  deleteModel,
  listModels,
  testModel,
  updateModel,
  updateModelStatus,
} from "@/api";
import { ApiErrorState } from "@/components/shared/ApiErrorState";
import { EmptyState } from "@/components/shared/EmptyState";
import { EntityListItem } from "@/components/shared/EntityListItem";
import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
import { MetricCard } from "@/components/shared/MetricCard";
import { PageHeader } from "@/components/shared/PageHeader";
import { SearchInput } from "@/components/shared/SearchInput";
import { StatusBadge } from "@/components/shared/StatusBadge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Dialog } from "@/components/ui/dialog";
import { Input, Textarea } from "@/components/ui/input";
import { Select } from "@/components/ui/select";
import { toast } from "@/components/ui/toaster";
import { formatDateTime } from "@/lib/utils";
import type { ModelCreateRequest, ModelDefinition, ModelStatus } from "@/types";

type ModelFormState = {
  code: string;
  name: string;
  provider_type: string;
  provider_base_url: string;
  provider_api_key: string;
  model_name: string;
  status: ModelStatus;
  description: string;
  capabilities: string;
  context_window: string;
  max_output_tokens: string;
  default_temperature: string;
  timeout_seconds: string;
};

const emptyForm: ModelFormState = {
  code: "",
  name: "",
  provider_type: "openai_compatible",
  provider_base_url: "http://127.0.0.1:11434/v1",
  provider_api_key: "",
  model_name: "",
  status: "active",
  description: "",
  capabilities: "chat",
  context_window: "",
  max_output_tokens: "",
  default_temperature: "",
  timeout_seconds: "60",
};

export function ModelsPage() {
  const [models, setModels] = React.useState<ModelDefinition[]>([]);
  const [selectedId, setSelectedId] = React.useState<string>();
  const [search, setSearch] = React.useState("");
  const [statusFilter, setStatusFilter] = React.useState("all");
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(false);
  const [testing, setTesting] = React.useState(false);
  const [error, setError] = React.useState<string>();
  const [createOpen, setCreateOpen] = React.useState(false);
  const [form, setForm] = React.useState<ModelFormState>(emptyForm);
  const [testPrompt, setTestPrompt] = React.useState("Say OK in one short sentence.");
  const [testOutput, setTestOutput] = React.useState<string>();

  const selected = models.find((model) => model.id === selectedId);
  const providers = Array.from(new Set(models.map((model) => model.provider_type))).sort();
  const activeCount = models.filter((model) => model.status === "active").length;
  const chatReadyCount = models.filter((model) => model.capabilities_json.includes("chat")).length;

  const filtered = models.filter((model) => {
    const haystack = `${model.name} ${model.code} ${model.model_name} ${model.provider_type}`.toLowerCase();
    const matchesSearch = haystack.includes(search.toLowerCase());
    const matchesStatus = statusFilter === "all" || model.status === statusFilter;
    return matchesSearch && matchesStatus;
  });

  const load = React.useCallback(async () => {
    setLoading(true);
    setError(undefined);
    try {
      const data = await listModels();
      setModels(data);
      setSelectedId((current) => current ?? data[0]?.id);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to load models");
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => {
    void load();
  }, [load]);

  React.useEffect(() => {
    if (selected) setForm(fromModel(selected));
  }, [selected]);

  async function createFromDialog(payload: ModelCreateRequest) {
    const created = await createModel(payload);
    setModels((current) => [created, ...current]);
    setSelectedId(created.id);
    setCreateOpen(false);
    toast.success("Model created");
  }

  async function saveSelected() {
    if (!selected) return;
    setSaving(true);
    try {
      const payload = toPayload(form);
      if (!form.provider_api_key.trim()) delete payload.provider_api_key;
      const updated = await updateModel(selected.id, payload);
      setModels((current) => current.map((model) => (model.id === updated.id ? updated : model)));
      toast.success("Model saved");
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Failed to save model");
    } finally {
      setSaving(false);
    }
  }

  async function toggleSelected() {
    if (!selected) return;
    const nextStatus: ModelStatus = selected.status === "active" ? "disabled" : "active";
    const updated = await updateModelStatus(selected.id, nextStatus);
    setModels((current) => current.map((model) => (model.id === updated.id ? updated : model)));
    toast.success(nextStatus === "active" ? "Model enabled" : "Model disabled");
  }

  async function deleteSelected() {
    if (!selected) return;
    await deleteModel(selected.id);
    setModels((current) => current.filter((model) => model.id !== selected.id));
    setSelectedId(models.find((model) => model.id !== selected.id)?.id);
    setTestOutput(undefined);
    toast.success("Model deleted");
  }

  async function runTest() {
    if (!selected) return;
    setTesting(true);
    setTestOutput(undefined);
    try {
      const result = await testModel(selected.id, { prompt: testPrompt, max_tokens: 128 });
      setTestOutput(result.response.content || JSON.stringify(result.response.raw_response_json, null, 2));
      toast.success("Model test completed");
    } catch (err) {
      setTestOutput(err instanceof Error ? err.message : "Model test failed");
      toast.error("Model test failed");
    } finally {
      setTesting(false);
    }
  }

  if (loading) return <LoadingSpinner label="Loading models" />;
  if (error) return <ApiErrorState message={error} onRetry={() => void load()} />;

  return (
    <div className="space-y-6">
      <PageHeader
        title="Models"
        description="Manage model providers, serving names, defaults, and connectivity tests."
        actions={
          <>
            <Button variant="outline" onClick={() => void load()}>
              <RefreshCw className="h-4 w-4" /> Refresh
            </Button>
            <Button onClick={() => setCreateOpen(true)}>
              <Plus className="h-4 w-4" /> New Model
            </Button>
          </>
        }
      />

      <div className="grid gap-4 md:grid-cols-3">
        <MetricCard label="Models" value={models.length} icon={Cpu} />
        <MetricCard label="Active" value={activeCount} icon={CheckCircle2} />
        <MetricCard label="Chat Ready" value={chatReadyCount} icon={Activity} />
      </div>

      <div className="grid gap-6 xl:grid-cols-[380px_1fr]">
        <Card>
          <CardHeader>
            <CardTitle>Model Catalog</CardTitle>
            <CardDescription>{filtered.length} of {models.length} shown</CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            <SearchInput value={search} onChange={setSearch} placeholder="Search models" />
            <Select
              value={statusFilter}
              onChange={(event) => setStatusFilter(event.target.value)}
              options={[
                { value: "all", label: "All statuses" },
                { value: "active", label: "Active" },
                { value: "disabled", label: "Disabled" },
              ]}
            />
            {filtered.length ? (
              <div className="space-y-2">
                {filtered.map((model) => (
                  <EntityListItem
                    key={model.id}
                    title={model.name}
                    subtitle={`${model.code} - ${model.model_name}`}
                    active={model.id === selectedId}
                    onClick={() => {
                      setSelectedId(model.id);
                      setTestOutput(undefined);
                    }}
                    meta={<StatusBadge status={model.status} />}
                  />
                ))}
              </div>
            ) : (
              <EmptyState icon={Cpu} title="No models" description="Create a model configuration to start routing chat completions." />
            )}
          </CardContent>
        </Card>

        {selected ? (
          <div className="space-y-6">
            <Card>
              <CardHeader>
                <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
                  <div>
                    <CardTitle>{selected.name}</CardTitle>
                    <CardDescription>
                      {selected.provider_type} / {selected.model_name}
                    </CardDescription>
                  </div>
                  <div className="flex flex-wrap gap-2">
                    <Button variant="outline" onClick={() => void toggleSelected()}>
                      <Power className="h-4 w-4" /> {selected.status === "active" ? "Disable" : "Enable"}
                    </Button>
                    <Button variant="destructive" onClick={() => void deleteSelected()}>
                      <Trash2 className="h-4 w-4" /> Delete
                    </Button>
                  </div>
                </div>
              </CardHeader>
              <CardContent>
                <ModelForm form={form} providers={providers} onChange={setForm} />
                <div className="mt-5 flex justify-end">
                  <Button onClick={() => void saveSelected()} disabled={saving}>
                    <Save className="h-4 w-4" /> {saving ? "Saving" : "Save"}
                  </Button>
                </div>
              </CardContent>
            </Card>

            <Card>
              <CardHeader>
                <CardTitle>Connectivity Test</CardTitle>
                <CardDescription>Send a short prompt through this exact provider configuration.</CardDescription>
              </CardHeader>
              <CardContent className="space-y-4">
                <Textarea value={testPrompt} onChange={(event) => setTestPrompt(event.target.value)} />
                <div className="flex justify-end">
                  <Button onClick={() => void runTest()} disabled={testing || selected.status !== "active"}>
                    <FlaskConical className="h-4 w-4" /> {testing ? "Testing" : "Test Model"}
                  </Button>
                </div>
                {testOutput ? (
                  <pre className="max-h-72 overflow-auto rounded-md border border-border bg-muted/40 p-3 text-sm whitespace-pre-wrap">
                    {testOutput}
                  </pre>
                ) : null}
                <div className="grid gap-2 text-sm text-muted-foreground sm:grid-cols-2">
                  <span>Updated {formatDateTime(selected.updated_time)}</span>
                  <span>{selected.has_provider_api_key ? "API key configured" : "No API key configured"}</span>
                </div>
              </CardContent>
            </Card>
          </div>
        ) : (
          <EmptyState icon={Cpu} title="No model selected" description="Select or create a model to edit its configuration." />
        )}
      </div>

      <CreateModelDialog
        open={createOpen}
        onOpenChange={setCreateOpen}
        onCreate={createFromDialog}
        providers={providers}
      />
    </div>
  );
}

function ModelForm({
  form,
  providers,
  onChange,
}: {
  form: ModelFormState;
  providers: string[];
  onChange: (form: ModelFormState) => void;
}) {
  const set = (key: keyof ModelFormState, value: string) => onChange({ ...form, [key]: value });
  const providerOptions = Array.from(new Set(["openai_compatible", "openai", "ollama", ...providers])).map((value) => ({
    value,
    label: value,
  }));
  return (
    <div className="grid gap-4 md:grid-cols-2">
      <Field label="Code"><Input value={form.code} onChange={(event) => set("code", event.target.value)} /></Field>
      <Field label="Name"><Input value={form.name} onChange={(event) => set("name", event.target.value)} /></Field>
      <Field label="Provider"><Select value={form.provider_type} onChange={(event) => set("provider_type", event.target.value)} options={providerOptions} /></Field>
      <Field label="Provider Base URL"><Input value={form.provider_base_url} onChange={(event) => set("provider_base_url", event.target.value)} /></Field>
      <Field label="Model Name"><Input value={form.model_name} onChange={(event) => set("model_name", event.target.value)} /></Field>
      <Field label="API Key"><Input type="password" value={form.provider_api_key} onChange={(event) => set("provider_api_key", event.target.value)} placeholder="Leave blank to keep unset" /></Field>
      <Field label="Capabilities"><Input value={form.capabilities} onChange={(event) => set("capabilities", event.target.value)} placeholder="chat, tools, vision" /></Field>
      <Field label="Status"><Select value={form.status} onChange={(event) => set("status", event.target.value)} options={[{ value: "active", label: "Active" }, { value: "disabled", label: "Disabled" }]} /></Field>
      <Field label="Context Window"><Input value={form.context_window} onChange={(event) => set("context_window", event.target.value)} inputMode="numeric" /></Field>
      <Field label="Max Output Tokens"><Input value={form.max_output_tokens} onChange={(event) => set("max_output_tokens", event.target.value)} inputMode="numeric" /></Field>
      <Field label="Default Temperature"><Input value={form.default_temperature} onChange={(event) => set("default_temperature", event.target.value)} inputMode="decimal" /></Field>
      <Field label="Timeout Seconds"><Input value={form.timeout_seconds} onChange={(event) => set("timeout_seconds", event.target.value)} inputMode="decimal" /></Field>
      <div className="md:col-span-2">
        <Field label="Description"><Textarea value={form.description} onChange={(event) => set("description", event.target.value)} /></Field>
      </div>
    </div>
  );
}

function CreateModelDialog({
  open,
  onOpenChange,
  onCreate,
  providers,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  onCreate: (payload: ModelCreateRequest) => Promise<void>;
  providers: string[];
}) {
  const [form, setForm] = React.useState<ModelFormState>(emptyForm);
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    if (open) setForm(emptyForm);
  }, [open]);

  async function submit() {
    setSaving(true);
    try {
      await onCreate(toPayload(form) as ModelCreateRequest);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Failed to create model");
    } finally {
      setSaving(false);
    }
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange} title="New Model" description="Add an OpenAI-compatible model endpoint." className="max-w-4xl">
      <ModelForm form={form} providers={providers} onChange={setForm} />
      <div className="mt-5 flex justify-end">
        <Button onClick={() => void submit()} disabled={saving}>
          <Plus className="h-4 w-4" /> {saving ? "Creating" : "Create Model"}
        </Button>
      </div>
    </Dialog>
  );
}

function Field({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <label className="space-y-1.5 text-sm font-medium">
      <span>{label}</span>
      {children}
    </label>
  );
}

function fromModel(model: ModelDefinition): ModelFormState {
  return {
    code: model.code,
    name: model.name,
    provider_type: model.provider_type,
    provider_base_url: model.provider_base_url,
    provider_api_key: "",
    model_name: model.model_name,
    status: model.status,
    description: model.description ?? "",
    capabilities: model.capabilities_json.join(", "),
    context_window: String(model.context_window ?? ""),
    max_output_tokens: String(model.max_output_tokens ?? ""),
    default_temperature: String(model.default_temperature ?? ""),
    timeout_seconds: String(model.timeout_seconds ?? 60),
  };
}

function toPayload(form: ModelFormState): ModelCreateRequest {
  return {
    code: form.code.trim(),
    name: form.name.trim(),
    provider_type: form.provider_type.trim() || "openai_compatible",
    provider_base_url: form.provider_base_url.trim(),
    provider_api_key: form.provider_api_key.trim() || null,
    model_name: form.model_name.trim(),
    status: form.status,
    description: form.description.trim() || null,
    capabilities_json: form.capabilities.split(",").map((item) => item.trim()).filter(Boolean),
    context_window: parseOptionalInteger(form.context_window),
    max_output_tokens: parseOptionalInteger(form.max_output_tokens),
    default_temperature: parseOptionalFloat(form.default_temperature),
    timeout_seconds: parseOptionalFloat(form.timeout_seconds) ?? 60,
    metadata_json: {},
  };
}

function parseOptionalInteger(value: string) {
  if (!value.trim()) return null;
  const parsed = Number.parseInt(value, 10);
  return Number.isFinite(parsed) ? parsed : null;
}

function parseOptionalFloat(value: string) {
  if (!value.trim()) return null;
  const parsed = Number.parseFloat(value);
  return Number.isFinite(parsed) ? parsed : null;
}
