"use client";

import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { api } from "@/lib/api";
import { getKeyLabel, type ProviderChoice } from "@/lib/deploy-preflight";

interface Props {
  open: boolean;
  /** Flat list of every candidate env var. Used as the fallback input
   *  set when `providers` is empty (or length 1). */
  missingKeys: string[];
  /** Grouped provider options derived from the template's models[] /
   *  required_env. When length ≥ 2 the modal shows a radio picker. */
  providers?: ProviderChoice[];
  /** Runtime slug — used only for the "The <runtime> runtime …"
   *  headline; behavior is driven by providers/missingKeys. */
  runtime: string;
  /** Called when all required keys for the chosen provider are saved. */
  onKeysAdded: () => void;
  /** Called when the user cancels the deploy. */
  onCancel: () => void;
  /** Optional — open the Settings Panel (Config tab → Secrets). */
  onOpenSettings?: () => void;
  /** If provided, secrets save at workspace scope instead of global. */
  workspaceId?: string;
}

interface KeyEntry {
  key: string;
  value: string;
  saved: boolean;
  saving: boolean;
  error: string | null;
}

/**
 * MissingKeysModal
 * ----------------
 * Dispatches between two modes based on what the template declares:
 *
 *  1. PROVIDER PICKER — when the preflight returned ≥2 `providers` (e.g.
 *     a Hermes template whose models[].required_env enumerate OpenRouter,
 *     Anthropic, Nous-native, etc.). Radio list of options, saving the
 *     chosen option's env vars satisfies the deploy.
 *
 *  2. ALL-KEYS — every entry in `missingKeys` rendered as its own input,
 *     all must save before Deploy. Used when the template has a single
 *     provider option or no declared alternatives.
 *
 * The modal never hardcodes per-runtime provider lists; the upstream
 * preflight derives that from the template config.yaml.
 */
export function MissingKeysModal({
  open,
  missingKeys,
  providers,
  runtime,
  onKeysAdded,
  onCancel,
  onOpenSettings,
  workspaceId,
}: Props) {
  const pickerProviders = providers ?? [];
  const pickerMode = pickerProviders.length > 1;

  if (pickerMode) {
    return (
      <ProviderPickerModal
        open={open}
        providers={pickerProviders}
        runtime={runtime}
        onKeysAdded={onKeysAdded}
        onCancel={onCancel}
        onOpenSettings={onOpenSettings}
        workspaceId={workspaceId}
      />
    );
  }

  // Prefer the (single) provider's envVars over the raw missingKeys when
  // we have one — the provider list is already de-duped and ordered.
  const keys =
    pickerProviders.length === 1 ? pickerProviders[0].envVars : missingKeys;

  return (
    <AllKeysModal
      open={open}
      missingKeys={keys}
      runtime={runtime}
      onKeysAdded={onKeysAdded}
      onCancel={onCancel}
      onOpenSettings={onOpenSettings}
      workspaceId={workspaceId}
    />
  );
}

// -----------------------------------------------------------------------------
// Provider-picker mode — choose one option, save its env var(s), deploy.
// -----------------------------------------------------------------------------

function ProviderPickerModal({
  open,
  providers,
  runtime,
  onKeysAdded,
  onCancel,
  onOpenSettings,
  workspaceId,
}: {
  open: boolean;
  providers: ProviderChoice[];
  runtime: string;
  onKeysAdded: () => void;
  onCancel: () => void;
  onOpenSettings?: () => void;
  workspaceId?: string;
}) {
  const [selectedId, setSelectedId] = useState(providers[0].id);
  const [entries, setEntries] = useState<KeyEntry[]>([]);
  const firstInputRef = useRef<HTMLInputElement>(null);

  const selected = useMemo(
    () => providers.find((p) => p.id === selectedId) ?? providers[0],
    [providers, selectedId],
  );

  useEffect(() => {
    if (!open) return;
    setSelectedId(providers[0].id);
  }, [open, providers]);

  useEffect(() => {
    if (!open) return;
    setEntries(
      selected.envVars.map((key) => ({
        key,
        value: "",
        saved: false,
        saving: false,
        error: null,
      })),
    );
  }, [open, selected]);

  useEffect(() => {
    if (!open) return;
    const raf = requestAnimationFrame(() => firstInputRef.current?.focus());
    return () => cancelAnimationFrame(raf);
  }, [open, selectedId]);

  useEffect(() => {
    if (!open) return;
    const handler = (e: KeyboardEvent) => {
      if (e.key === "Escape") onCancel();
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [open, onCancel]);

  const updateEntry = useCallback(
    (index: number, updates: Partial<KeyEntry>) => {
      setEntries((prev) =>
        prev.map((e, i) => (i === index ? { ...e, ...updates } : e)),
      );
    },
    [],
  );

  const handleSaveKey = useCallback(
    async (index: number) => {
      const entry = entries[index];
      if (!entry.value.trim()) return;
      updateEntry(index, { saving: true, error: null });
      try {
        if (workspaceId) {
          await api.put(`/workspaces/${workspaceId}/secrets`, {
            key: entry.key,
            value: entry.value.trim(),
          });
        } else {
          await api.put("/settings/secrets", {
            key: entry.key,
            value: entry.value.trim(),
          });
        }
        updateEntry(index, { saved: true, saving: false });
      } catch (e) {
        updateEntry(index, {
          saving: false,
          error: e instanceof Error ? e.message : "Failed to save",
        });
      }
    },
    [entries, updateEntry, workspaceId],
  );

  if (!open) return null;

  const allSaved = entries.length > 0 && entries.every((e) => e.saved);
  const anySaving = entries.some((e) => e.saving);
  const runtimeLabel = runtime
    .replace(/[-_]/g, " ")
    .replace(/\b\w/g, (c) => c.toUpperCase());

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center">
      <div
        aria-hidden="true"
        className="absolute inset-0 bg-black/70 backdrop-blur-sm"
        onClick={onCancel}
      />

      <div
        role="dialog"
        aria-modal="true"
        aria-labelledby="missing-keys-title"
        className="relative bg-zinc-900 border border-zinc-700 rounded-xl shadow-2xl shadow-black/50 max-w-[480px] w-full mx-4 overflow-hidden"
      >
        <div className="px-5 py-4 border-b border-zinc-800">
          <div className="flex items-center gap-2 mb-1">
            <div
              className="w-5 h-5 rounded-md bg-amber-600/20 border border-amber-500/30 flex items-center justify-center"
              aria-hidden="true"
            >
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
                <path d="M6 1L11 10H1L6 1Z" stroke="#fbbf24" strokeWidth="1.2" strokeLinejoin="round" />
                <path d="M6 5V7" stroke="#fbbf24" strokeWidth="1.2" strokeLinecap="round" />
                <circle cx="6" cy="8.5" r="0.5" fill="#fbbf24" />
              </svg>
            </div>
            <h3 id="missing-keys-title" className="text-sm font-semibold text-zinc-100">
              Missing API Keys
            </h3>
          </div>
          <p className="text-[12px] text-zinc-400 leading-relaxed">
            The <span className="text-amber-300 font-medium">{runtimeLabel}</span>{" "}
            runtime supports multiple providers. Pick one and paste its API key.
          </p>
        </div>

        <div className="px-5 py-4 space-y-3">
          <fieldset className="space-y-1.5">
            <legend className="text-[10px] uppercase tracking-wide text-zinc-500 font-semibold mb-1.5">
              Provider
            </legend>
            {providers.map((p) => (
              <label
                key={p.id}
                className={`flex items-start gap-2.5 rounded-lg border px-3 py-2 cursor-pointer transition-colors ${
                  selectedId === p.id
                    ? "bg-blue-600/15 border-blue-500/50"
                    : "bg-zinc-800/40 border-zinc-700/50 hover:border-zinc-600"
                }`}
              >
                <input
                  type="radio"
                  name="provider"
                  value={p.id}
                  checked={selectedId === p.id}
                  onChange={() => setSelectedId(p.id)}
                  className="mt-0.5 accent-blue-500"
                />
                <div className="min-w-0 flex-1">
                  <div className="text-[12px] text-zinc-100 font-medium">{p.label}</div>
                  <div className="text-[10px] font-mono text-zinc-500">
                    {p.envVars.join(", ")}
                  </div>
                  {p.note && (
                    <div className="text-[10px] text-zinc-500 mt-1 leading-relaxed">
                      {p.note}
                    </div>
                  )}
                </div>
              </label>
            ))}
          </fieldset>

          <div className="space-y-2">
            {entries.map((entry, index) => (
              <div
                key={entry.key}
                className="bg-zinc-800/50 rounded-lg px-3 py-2.5 border border-zinc-700/50"
              >
                <div className="flex items-center justify-between mb-1.5">
                  <div>
                    <div className="text-[11px] text-zinc-300 font-medium">
                      {getKeyLabel(entry.key)}
                    </div>
                    <div className="text-[9px] font-mono text-zinc-500">{entry.key}</div>
                  </div>
                  {entry.saved && (
                    <span className="text-[9px] text-emerald-400 bg-emerald-900/30 px-1.5 py-0.5 rounded flex items-center gap-1">
                      <svg width="8" height="8" viewBox="0 0 8 8" fill="none" aria-hidden="true">
                        <path d="M1.5 4L3.5 6L6.5 2" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" />
                      </svg>
                      Saved
                    </span>
                  )}
                </div>

                {!entry.saved && (
                  <div className="flex gap-2 mt-2">
                    <input
                      value={entry.value}
                      onChange={(e) => updateEntry(index, { value: e.target.value.trimStart() })}
                      placeholder={entry.key.includes("API_KEY") ? "sk-..." : "Enter value"}
                      type="password"
                      ref={index === 0 ? firstInputRef : undefined}
                      onKeyDown={(e) => {
                        if (e.key === "Enter" && entry.value.trim()) {
                          handleSaveKey(index);
                        }
                      }}
                      className="flex-1 bg-zinc-900 border border-zinc-600 rounded px-2 py-1.5 text-[11px] text-zinc-100 font-mono focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500/20 transition-colors"
                    />
                    <button
                      onClick={() => handleSaveKey(index)}
                      disabled={!entry.value.trim() || entry.saving}
                      className="px-3 py-1.5 bg-blue-600 hover:bg-blue-500 text-[11px] rounded text-white disabled:opacity-30 transition-colors shrink-0"
                    >
                      {entry.saving ? "..." : "Save"}
                    </button>
                  </div>
                )}

                {entry.error && (
                  <div className="mt-1.5 text-[10px] text-red-400">{entry.error}</div>
                )}
              </div>
            ))}
          </div>
        </div>

        <div className="px-5 py-3 border-t border-zinc-800 bg-zinc-950/50 flex items-center justify-between gap-2">
          <div>
            {onOpenSettings && (
              <button
                onClick={onOpenSettings}
                className="text-[11px] text-blue-400 hover:text-blue-300 transition-colors"
              >
                Open Settings Panel
              </button>
            )}
          </div>
          <div className="flex items-center gap-2">
            <button
              onClick={onCancel}
              className="px-3.5 py-1.5 text-[12px] text-zinc-400 hover:text-zinc-200 bg-zinc-800 hover:bg-zinc-700 border border-zinc-700 rounded-lg transition-colors"
            >
              Cancel Deploy
            </button>
            <button
              onClick={onKeysAdded}
              disabled={!allSaved || anySaving}
              className="px-3.5 py-1.5 text-[12px] bg-blue-600 hover:bg-blue-500 text-white rounded-lg transition-colors disabled:opacity-40"
            >
              {allSaved ? "Deploy" : entries.length > 1 ? "Add Keys" : "Add Key"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

// -----------------------------------------------------------------------------
// All-keys mode — every missingKey rendered as its own input, all required.
// -----------------------------------------------------------------------------

function AllKeysModal({
  open,
  missingKeys,
  runtime,
  onKeysAdded,
  onCancel,
  onOpenSettings,
  workspaceId,
}: {
  open: boolean;
  missingKeys: string[];
  runtime: string;
  onKeysAdded: () => void;
  onCancel: () => void;
  onOpenSettings?: () => void;
  workspaceId?: string;
}) {
  const [entries, setEntries] = useState<KeyEntry[]>([]);
  const [globalError, setGlobalError] = useState<string | null>(null);

  useEffect(() => {
    if (!open) return;
    setEntries(
      missingKeys.map((key) => ({
        key,
        value: "",
        saved: false,
        saving: false,
        error: null,
      })),
    );
    setGlobalError(null);
  }, [open, missingKeys]);

  useEffect(() => {
    if (!open) return;
    const handler = (e: KeyboardEvent) => {
      if (e.key === "Escape") onCancel();
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [open, onCancel]);

  const updateEntry = useCallback(
    (index: number, updates: Partial<KeyEntry>) => {
      setEntries((prev) =>
        prev.map((entry, i) => (i === index ? { ...entry, ...updates } : entry)),
      );
    },
    [],
  );

  const handleSaveKey = useCallback(
    async (index: number) => {
      const entry = entries[index];
      if (!entry.value.trim()) return;

      updateEntry(index, { saving: true, error: null });

      try {
        if (workspaceId) {
          await api.put(`/workspaces/${workspaceId}/secrets`, {
            key: entry.key,
            value: entry.value.trim(),
          });
        } else {
          await api.put("/settings/secrets", {
            key: entry.key,
            value: entry.value.trim(),
          });
        }
        updateEntry(index, { saved: true, saving: false });
      } catch (e) {
        updateEntry(index, {
          saving: false,
          error: e instanceof Error ? e.message : "Failed to save",
        });
      }
    },
    [entries, updateEntry, workspaceId],
  );

  const handleAddKeysAndDeploy = useCallback(() => {
    const anySaving = entries.some((e) => e.saving);
    if (anySaving) {
      setGlobalError("Please wait for all keys to finish saving.");
      return;
    }
    const allSaved = entries.every((e) => e.saved);
    if (!allSaved) {
      setGlobalError("Please save all required keys before deploying.");
      return;
    }
    onKeysAdded();
  }, [entries, onKeysAdded]);

  // Focus trap: auto-focus first input when modal opens
  useEffect(() => {
    if (!open) return;
    const timer = requestAnimationFrame(() => {
      document.getElementById("missing-keys-title")?.focus();
    });
    return () => cancelAnimationFrame(timer);
  }, [open]);

  if (!open) return null;

  const allSaved = entries.length > 0 && entries.every((e) => e.saved);
  const anySaving = entries.some((e) => e.saving);
  const runtimeLabel = runtime
    .replace(/[-_]/g, " ")
    .replace(/\b\w/g, (c) => c.toUpperCase());

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center">
      <div
        className="absolute inset-0 bg-black/70 backdrop-blur-sm"
        aria-hidden="true"
        onClick={onCancel}
      />

      <div
        role="dialog"
        aria-modal="true"
        aria-labelledby="missing-keys-title"
        className="relative bg-zinc-900 border border-zinc-700 rounded-xl shadow-2xl shadow-black/50 max-w-[440px] w-full mx-4 overflow-hidden"
      >
        <div className="px-5 py-4 border-b border-zinc-800">
          <div className="flex items-center gap-2 mb-1">
            <div
              className="w-5 h-5 rounded-md bg-amber-600/20 border border-amber-500/30 flex items-center justify-center"
              aria-hidden="true"
            >
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
                <path d="M6 1L11 10H1L6 1Z" stroke="#fbbf24" strokeWidth="1.2" strokeLinejoin="round" />
                <path d="M6 5V7" stroke="#fbbf24" strokeWidth="1.2" strokeLinecap="round" />
                <circle cx="6" cy="8.5" r="0.5" fill="#fbbf24" />
              </svg>
            </div>
            <h3 id="missing-keys-title" className="text-sm font-semibold text-zinc-100">
              Missing API Keys
            </h3>
          </div>
          <p className="text-[12px] text-zinc-400 leading-relaxed">
            The <span className="text-amber-300 font-medium">{runtimeLabel}</span>{" "}
            runtime requires the following keys to be configured before deploying.
          </p>
        </div>

        <div className="px-5 py-4 space-y-3 max-h-[50vh] overflow-y-auto">
          {entries.map((entry, index) => (
            <div
              key={entry.key}
              className="bg-zinc-800/50 rounded-lg px-3 py-2.5 border border-zinc-700/50"
            >
              <div className="flex items-center justify-between mb-1">
                <div>
                  <div className="text-[11px] text-zinc-300 font-medium">
                    {getKeyLabel(entry.key)}
                  </div>
                  <div className="text-[9px] font-mono text-zinc-500">{entry.key}</div>
                </div>
                {entry.saved && (
                  <span className="text-[9px] text-emerald-400 bg-emerald-900/30 px-1.5 py-0.5 rounded flex items-center gap-1">
                    <svg width="8" height="8" viewBox="0 0 8 8" fill="none">
                      <path d="M1.5 4L3.5 6L6.5 2" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" />
                    </svg>
                    Saved
                  </span>
                )}
              </div>

              {!entry.saved && (
                <div className="flex gap-2 mt-2">
                  <input
                    value={entry.value}
                    onChange={(e) => updateEntry(index, { value: e.target.value.trimStart() })}
                    placeholder={entry.key.includes("API_KEY") ? "sk-..." : "Enter value"}
                    type="password"
                    autoFocus={index === 0}
                    onKeyDown={(e) => {
                      if (e.key === "Enter" && entry.value.trim()) {
                        handleSaveKey(index);
                      }
                    }}
                    className="flex-1 bg-zinc-900 border border-zinc-600 rounded px-2 py-1.5 text-[11px] text-zinc-100 font-mono focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500/20 transition-colors"
                  />
                  <button
                    type="button"
                    onClick={() => handleSaveKey(index)}
                    disabled={!entry.value.trim() || entry.saving}
                    className="px-3 py-1.5 bg-blue-600 hover:bg-blue-500 text-[11px] rounded text-white disabled:opacity-30 transition-colors shrink-0"
                  >
                    {entry.saving ? "..." : "Save"}
                  </button>
                </div>
              )}

              {entry.error && <div className="mt-1.5 text-[10px] text-red-400">{entry.error}</div>}
            </div>
          ))}

          {globalError && (
            <div className="px-3 py-2 bg-red-950/40 border border-red-800/50 rounded-lg text-[11px] text-red-400">
              {globalError}
            </div>
          )}
        </div>

        <div className="px-5 py-3 border-t border-zinc-800 bg-zinc-950/50 flex items-center justify-between gap-2">
          <div>
            {onOpenSettings && (
              <button
                type="button"
                onClick={onOpenSettings}
                className="text-[11px] text-blue-400 hover:text-blue-300 transition-colors"
              >
                Open Settings Panel
              </button>
            )}
          </div>
          <div className="flex items-center gap-2">
            <button
              type="button"
              onClick={onCancel}
              className="px-3.5 py-1.5 text-[12px] text-zinc-400 hover:text-zinc-200 bg-zinc-800 hover:bg-zinc-700 border border-zinc-700 rounded-lg transition-colors"
            >
              Cancel Deploy
            </button>
            <button
              type="button"
              onClick={handleAddKeysAndDeploy}
              disabled={!allSaved || anySaving}
              className="px-3.5 py-1.5 text-[12px] bg-blue-600 hover:bg-blue-500 text-white rounded-lg transition-colors disabled:opacity-40"
            >
              {anySaving ? "Saving..." : allSaved ? "Deploy" : "Add Keys"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}
