"use client";

import { useEffect, useRef, useState } from "react";
import { ApiError, del, get, post, put } from "@/lib/api";
import { Badge, Field, InfoTip, Notice, Spinner } from "@/components/ui";
import { PIPELINE_STEP_INFO } from "@/lib/glossary";

type CredentialStatus = {
  configured: boolean;
  projectIdSet: boolean;
  fileExists: boolean;
  valid: boolean;
  clientEmail: string | null;
  path: string;
};
type VertexData = {
  projectId: string;
  location: string;
  locations: Record<string, string>;
  locationOverrides: Record<string, string>;
  models: Record<string, string>;
  steps: string[];
  suggestedModels: Record<string, string[]>;
  credentialStatus: CredentialStatus;
};
type SmokeStep = { step: string; ok: boolean; detail: string | null; error: string | null };
type SmokeResult = { provider: string; segmentation: string; ok: boolean; steps: SmokeStep[] };

const STEP_LABELS: Record<string, string> = {
  research: "Research",
  manipulation: "Manipulation",
  scoring: "Scoring / BRC",
  alt_text: "Alt text",
  image: "Image creation",
  segmentation: "Segmentation",
  video: "Video (Veo)",
};

// Common Vertex regions for the per-step region picker (free text also allowed).
const REGION_SUGGESTIONS = [
  "global",
  "europe-west1",
  "europe-west3",
  "europe-west4",
  "europe-west6",
  "us-central1",
];

export default function VertexSettings() {
  const [data, setData] = useState<VertexData | null>(null);
  const [projectId, setProjectId] = useState("");
  const [location, setLocation] = useState("");
  const [models, setModels] = useState<Record<string, string>>({});
  const [locations, setLocations] = useState<Record<string, string>>({});
  const [errors, setErrors] = useState<string[]>([]);
  const [saved, setSaved] = useState(false);
  const [busy, setBusy] = useState(false);
  const [smoke, setSmoke] = useState<SmokeResult | null>(null);
  const [smokeBusy, setSmokeBusy] = useState(false);
  const [loadError, setLoadError] = useState<string | null>(null);
  const [keyBusy, setKeyBusy] = useState(false);
  const [keyMsg, setKeyMsg] = useState<string | null>(null);
  const fileRef = useRef<HTMLInputElement>(null);

  function hydrate(d: VertexData) {
    setData(d);
    setProjectId(d.projectId);
    setLocation(d.location);
    setModels(d.models);
    // Hydrate from RAW overrides so a blank field means "inherit the default region".
    setLocations(d.locationOverrides ?? {});
  }

  useEffect(() => {
    get<VertexData>("vertex").then(hydrate).catch((e) => setLoadError((e as Error).message));
  }, []);

  if (loadError) return <Notice kind="error">Could not load Vertex config: {loadError}</Notice>;
  if (!data) return <Spinner label="Loading Vertex configuration…" />;

  const cred = data.credentialStatus;

  async function save(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setBusy(true);
    setSaved(false);
    setErrors([]);
    try {
      hydrate(await put<VertexData>("vertex", { projectId, location, models, locations }));
      setSaved(true);
    } catch (err) {
      setErrors(err instanceof ApiError && err.errors.length ? err.errors : [(err as Error).message]);
    } finally {
      setBusy(false);
    }
  }

  async function uploadKey(file: File) {
    setKeyBusy(true);
    setKeyMsg(null);
    setErrors([]);
    try {
      const text = await file.text();
      hydrate(await post<VertexData>("vertex/credentials", { credentials: text }));
      setKeyMsg("Service-account key stored.");
    } catch (err) {
      setErrors(err instanceof ApiError && err.errors.length ? err.errors : [(err as Error).message]);
    } finally {
      setKeyBusy(false);
      if (fileRef.current) fileRef.current.value = "";
    }
  }

  async function removeKey() {
    setKeyBusy(true);
    setKeyMsg(null);
    try {
      hydrate(await del<VertexData>("vertex/credentials"));
      setKeyMsg("Service-account key removed.");
    } catch (err) {
      setErrors([(err as Error).message]);
    } finally {
      setKeyBusy(false);
    }
  }

  async function runSmoke() {
    setSmokeBusy(true);
    setSmoke(null);
    try {
      setSmoke(await post<SmokeResult>("vertex/smoke"));
    } catch (err) {
      setErrors([(err as Error).message]);
    } finally {
      setSmokeBusy(false);
    }
  }

  return (
    <section aria-labelledby="vertex-h" className="space-y-6">
      <h2 id="vertex-h" className="text-xl font-semibold text-slate-900">Vertex AI</h2>
      <p className="-mt-4 text-sm text-slate-600">
        The AI backbone (research, manipulation, scoring, alt text, image creation, segmentation). Project, models and a region
        <em>per step</em> apply without a redeploy — handy when a model is only served in some regions (e.g. Gemini is not in
        <code className="mx-1">europe-west6</code>, so you can pin image/text steps to <code>europe-west4</code> or <code>global</code>).
        The service-account key stays a mounted secret.
      </p>

      <div className="card" aria-labelledby="cred-h">
        <h3 id="cred-h" className="mb-2 font-semibold text-slate-900">Credential status</h3>
        {cred.configured ? (
          <Notice kind="success">
            Vertex credentials detected — service account <strong>{cred.clientEmail}</strong>. The AI pipeline is live.
          </Notice>
        ) : (
          <Notice kind="info">
            Vertex is not configured yet, so AI calls will fail with a clear message until you set it up.{" "}
            {cred.projectIdSet ? "Project id set." : "Set a project id below."}{" "}
            {cred.fileExists ? (cred.valid ? "Key file valid." : "Key file is not a valid service-account JSON.") : "No key file found."}
          </Notice>
        )}
        <dl className="mt-3 grid grid-cols-2 gap-y-1 text-sm sm:grid-cols-4">
          <Status label="Project id" ok={cred.projectIdSet} />
          <Status label="Key file present" ok={cred.fileExists} />
          <Status label="Key valid" ok={cred.valid} />
          <Status label="Configured" ok={cred.configured} />
        </dl>
        <p className="mt-3 text-xs text-slate-500">
          The key is stored server-side at <code>{cred.path}</code> and never shown again. You can upload it here, or mount it as a file.
        </p>

        <div className="mt-3 flex flex-wrap items-center gap-3">
          <label htmlFor="vertex-key" className="btn-secondary cursor-pointer">
            {cred.fileExists ? "Replace key…" : "Upload service-account key…"}
            <input
              ref={fileRef}
              id="vertex-key"
              type="file"
              accept="application/json,.json"
              className="sr-only"
              onChange={(e) => {
                const f = e.target.files?.[0];
                if (f) void uploadKey(f);
              }}
              disabled={keyBusy}
            />
          </label>
          {cred.fileExists && (
            <button type="button" className="btn-danger" onClick={removeKey} disabled={keyBusy}>
              Remove key
            </button>
          )}
          {keyBusy && <span className="text-sm text-slate-500" role="status">Working…</span>}
          {keyMsg && <span className="text-sm text-ok" role="status">{keyMsg}</span>}
        </div>
      </div>

      {saved && <Notice kind="success">Vertex configuration saved.</Notice>}
      {errors.length > 0 && <Notice kind="error"><ul className="list-disc pl-5">{errors.map((er, i) => <li key={i}>{er}</li>)}</ul></Notice>}

      <form onSubmit={save} className="space-y-6">
        <div className="card grid gap-5 sm:grid-cols-2">
          <Field label="GCP project id" htmlFor="projectId" hint="e.g. digt-vertex-poc">
            <input id="projectId" value={projectId} onChange={(e) => setProjectId(e.target.value)} className="input" autoComplete="off" />
          </Field>
          <Field label="Default region" htmlFor="location" hint="Fallback for any step left blank below. Swiss residency: europe-west6">
            <input id="location" list="region-suggest" value={location} onChange={(e) => setLocation(e.target.value)} className="input" autoComplete="off" />
          </Field>
        </div>

        <fieldset className="card grid gap-4 sm:grid-cols-2">
          <legend className="px-1 text-sm font-semibold text-slate-900">Model &amp; region per pipeline step</legend>
          <p className="-mt-1 text-xs text-slate-500 sm:col-span-2">
            Leave a region blank to inherit the default (<code>{location || "europe-west6"}</code>). Use <code>global</code> for the
            widest model availability, or a specific region for data residency.
          </p>
          {data.steps.map((step) => (
            <div key={step} className="rounded-lg border border-slate-200 p-3">
              <div className="mb-2 flex items-center gap-1.5 text-sm font-semibold text-slate-800">
                {STEP_LABELS[step] || step}
                {PIPELINE_STEP_INFO[step] && (
                  <InfoTip label={`About ${STEP_LABELS[step] || step}`}>{PIPELINE_STEP_INFO[step]}</InfoTip>
                )}
              </div>
              <Field label="Model" htmlFor={`model-${step}`}>
                <input
                  id={`model-${step}`}
                  list={`suggest-${step}`}
                  value={models[step] ?? ""}
                  onChange={(e) => setModels({ ...models, [step]: e.target.value })}
                  className="input font-mono text-xs"
                  autoComplete="off"
                />
                <datalist id={`suggest-${step}`}>
                  {(data.suggestedModels[step] || []).map((m) => <option key={m} value={m} />)}
                </datalist>
              </Field>
              <div className="mt-2">
                <Field label="Region" htmlFor={`region-${step}`} hint={`effective: ${locations[step]?.trim() || location || "europe-west6"}`}>
                  <input
                    id={`region-${step}`}
                    list="region-suggest"
                    value={locations[step] ?? ""}
                    placeholder={`inherits ${location || "europe-west6"}`}
                    onChange={(e) => setLocations({ ...locations, [step]: e.target.value })}
                    className="input font-mono text-xs"
                    autoComplete="off"
                  />
                </Field>
              </div>
            </div>
          ))}
          <datalist id="region-suggest">
            {REGION_SUGGESTIONS.map((r) => <option key={r} value={r} />)}
          </datalist>
        </fieldset>

        <div className="flex flex-wrap gap-3">
          <button type="submit" className="btn-primary" disabled={busy}>{busy ? "Saving…" : "Save Vertex configuration"}</button>
          <button type="button" className="btn-secondary" onClick={runSmoke} disabled={smokeBusy}>{smokeBusy ? "Running smoke test…" : "Run smoke test"}</button>
        </div>
      </form>

      {smoke && (
        <div className="card" aria-live="polite">
          <h3 className="mb-3 font-semibold text-slate-900">
            Smoke test — provider <Badge kind="neutral">{smoke.provider}</Badge>{" "}
            {smoke.ok ? <Badge kind="ok">all steps passed</Badge> : <Badge kind="danger">failures</Badge>}
          </h3>
          <ul className="space-y-1 text-sm">
            {smoke.steps.map((s) => (
              <li key={s.step} className="flex items-start gap-2">
                <span className="w-28 font-medium text-slate-700">{STEP_LABELS[s.step] || s.step}</span>
                {s.ok ? <Badge kind="ok">OK</Badge> : <Badge kind="danger">ERR</Badge>}
                <span className="text-slate-600">{s.detail || s.error}</span>
              </li>
            ))}
          </ul>
        </div>
      )}
    </section>
  );
}

function Status({ label, ok }: { label: string; ok: boolean }) {
  return (
    <div className="flex items-center gap-2">
      <span aria-hidden className={ok ? "text-ok" : "text-slate-400"}>{ok ? "●" : "○"}</span>
      <span className="text-slate-700">
        {label}: <span className="sr-only">{ok ? "yes" : "no"}</span>
        <span aria-hidden className={ok ? "text-ok" : "text-slate-500"}>{ok ? "yes" : "no"}</span>
      </span>
    </div>
  );
}
