"use client";

import * as React from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { apiFetch } from "@/lib/api";
import type { OllamaModelInfo } from "@/lib/ollama";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Download, Trash2, Loader2, AlertTriangle } from "lucide-react";
import { formatBytes } from "@/lib/utils";
import { toast } from "sonner";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";

interface ModelsResponse {
  models: OllamaModelInfo[];
}

interface PullProgress {
  status: string;
  total?: number;
  completed?: number;
  digest?: string;
  error?: string;
}

export function ModelsSettings() {
  const qc = useQueryClient();
  const modelsQuery = useQuery({
    queryKey: ["models"],
    queryFn: () => apiFetch<ModelsResponse>("/api/models"),
    retry: false,
  });

  const [pullName, setPullName] = React.useState("");
  const [pulling, setPulling] = React.useState(false);
  const [pullProgress, setPullProgress] = React.useState<PullProgress | null>(null);
  const abortRef = React.useRef<AbortController | null>(null);

  async function startPull() {
    const name = pullName.trim();
    if (!name) return;
    setPulling(true);
    setPullProgress({ status: "starting" });
    const ctl = new AbortController();
    abortRef.current = ctl;

    try {
      const res = await fetch("/api/models/pull", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ name }),
        signal: ctl.signal,
      });
      if (!res.ok || !res.body) throw new Error("Failed to start pull");

      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = "";
      outer: while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        let nl: number;
        while ((nl = buffer.indexOf("\n\n")) >= 0) {
          const block = buffer.slice(0, nl);
          buffer = buffer.slice(nl + 2);
          const line = block.trim();
          if (!line.startsWith("data:")) continue;
          const payload = line.slice(5).trim();
          try {
            const evt = JSON.parse(payload) as PullProgress | { status: "done" };
            if ("error" in evt && evt.error) {
              toast.error(evt.error);
              break outer;
            }
            setPullProgress(evt as PullProgress);
            if (evt.status === "success" || evt.status === "done") {
              toast.success(`Pulled ${name}`);
              break outer;
            }
          } catch {
            /* ignore malformed */
          }
        }
      }
    } catch (e) {
      if ((e as Error).name !== "AbortError") {
        toast.error(e instanceof Error ? e.message : "Pull failed");
      }
    } finally {
      setPulling(false);
      abortRef.current = null;
      qc.invalidateQueries({ queryKey: ["models"] });
      setTimeout(() => setPullProgress(null), 2000);
    }
  }

  function stopPull() {
    abortRef.current?.abort();
  }

  const [confirmDelete, setConfirmDelete] = React.useState<string | null>(null);
  async function doDelete(name: string) {
    try {
      await apiFetch(`/api/models/${encodeURIComponent(name)}`, { method: "DELETE" });
      toast.success(`Deleted ${name}`);
      qc.invalidateQueries({ queryKey: ["models"] });
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Delete failed");
    } finally {
      setConfirmDelete(null);
    }
  }

  const models = modelsQuery.data?.models ?? [];
  const ollamaOffline = modelsQuery.isError;
  const pct =
    pullProgress?.total && pullProgress.completed
      ? Math.min(100, Math.floor((pullProgress.completed / pullProgress.total) * 100))
      : null;

  return (
    <div className="space-y-6">
      <Card>
        <CardHeader>
          <CardTitle>Pull a model</CardTitle>
          <CardDescription>
            Enter an Ollama model name, e.g. <code className="rounded bg-muted px-1 py-0.5 text-xs">llama3.2:3b</code>
          </CardDescription>
        </CardHeader>
        <CardContent className="space-y-3">
          <div className="flex gap-2">
            <Input
              placeholder="model:tag"
              value={pullName}
              onChange={(e) => setPullName(e.target.value)}
              disabled={pulling}
              onKeyDown={(e) => {
                if (e.key === "Enter") startPull();
              }}
            />
            {pulling ? (
              <Button variant="destructive" onClick={stopPull}>
                Cancel
              </Button>
            ) : (
              <Button onClick={startPull} disabled={!pullName.trim()}>
                <Download />
                Pull
              </Button>
            )}
          </div>
          {pullProgress && (
            <div className="rounded-lg border border-border bg-muted/30 p-3">
              <div className="mb-2 flex items-center justify-between text-xs">
                <span className="font-medium">{pullProgress.status}</span>
                {pct !== null && <span className="font-mono">{pct}%</span>}
              </div>
              {pct !== null && (
                <div className="h-1.5 w-full overflow-hidden rounded-full bg-muted">
                  <div
                    className="h-full bg-primary transition-all duration-200"
                    style={{ width: `${pct}%` }}
                  />
                </div>
              )}
              {pullProgress.total && pullProgress.completed && (
                <p className="mt-2 text-xs text-muted-foreground">
                  {formatBytes(pullProgress.completed)} / {formatBytes(pullProgress.total)}
                </p>
              )}
            </div>
          )}
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle>Installed models</CardTitle>
          <CardDescription>
            {ollamaOffline
              ? "Ollama appears offline."
              : models.length === 0
              ? "Nothing installed yet."
              : `${models.length} model${models.length === 1 ? "" : "s"} available.`}
          </CardDescription>
        </CardHeader>
        <CardContent>
          {ollamaOffline ? (
            <div className="flex items-center gap-2 rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive">
              <AlertTriangle className="h-4 w-4" />
              Could not reach Ollama. Check the URL in General settings.
            </div>
          ) : modelsQuery.isLoading ? (
            <div className="flex items-center gap-2 text-sm text-muted-foreground">
              <Loader2 className="h-4 w-4 animate-spin" />
              Loading models…
            </div>
          ) : (
            <div className="divide-y divide-border">
              {models.map((m) => (
                <div key={m.name} className="flex items-center justify-between gap-4 py-3">
                  <div className="min-w-0">
                    <div className="flex items-center gap-2">
                      <span className="font-mono text-sm font-medium">{m.name}</span>
                      {m.details?.parameter_size && (
                        <Badge variant="muted">{m.details.parameter_size}</Badge>
                      )}
                      {m.details?.quantization_level && (
                        <Badge variant="outline">{m.details.quantization_level}</Badge>
                      )}
                    </div>
                    <p className="mt-0.5 text-xs text-muted-foreground">
                      {formatBytes(m.size)} · modified {new Date(m.modified_at).toLocaleDateString()}
                    </p>
                  </div>
                  <Button
                    variant="ghost"
                    size="icon-sm"
                    onClick={() => setConfirmDelete(m.name)}
                    aria-label="Delete model"
                  >
                    <Trash2 />
                  </Button>
                </div>
              ))}
            </div>
          )}
        </CardContent>
      </Card>

      <Dialog open={!!confirmDelete} onOpenChange={(o) => !o && setConfirmDelete(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Delete model?</DialogTitle>
            <DialogDescription>
              <span className="font-mono">{confirmDelete}</span> will be removed from Ollama.
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button variant="ghost" onClick={() => setConfirmDelete(null)}>
              Cancel
            </Button>
            <Button variant="destructive" onClick={() => confirmDelete && doDelete(confirmDelete)}>
              Delete
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
