<script lang="ts">
  import { onDestroy, onMount } from "svelte";
  import { AlertCircle, Bot, Check, Cpu, Eye, EyeOff, Gauge, Globe, Key, LoaderCircle, Sparkles, X, Zap } from "@lucide/svelte";
  import { credDelete, credLoad, credSave } from "../git";
  import type { AiSettings, CommitAiLocalProfile, CommitAiProvider, LocalModelOption } from "../types";
  import SelectMenu from "./SelectMenu.svelte";

  interface Props {
    settings: AiSettings;
    localModels: LocalModelOption[];
    onSave: (settings: AiSettings) => void;
    onClose: () => void;
  }

  let { settings, localModels = [], onSave, onClose }: Props = $props();

  type CloudProvider = Exclude<CommitAiProvider, "local">;

  const CRED_KEYS: Record<CloudProvider, string> = {
    openai: "ai:openai",
    anthropic: "ai:anthropic",
    custom: "ai:custom",
  };

  let provider = $state<CommitAiProvider>("local");
  let localModelId = $state("");
  let localProfile = $state<CommitAiLocalProfile>("fast");
  let openaiModel = $state("");
  let anthropicModel = $state("");
  let customBaseUrl = $state("");
  let customModel = $state("");

  let openaiApiKey = $state("");
  let anthropicApiKey = $state("");
  let customApiKey = $state("");
  let showKey = $state(false);
  let loadingKeys = $state(true);
  let saving = $state(false);
  let error = $state("");
  let errorHideTimer: ReturnType<typeof setTimeout> | undefined;

  $effect(() => {
    provider = settings.provider;
    localModelId = settings.localModelId;
    localProfile = settings.localProfile ?? "fast";
    openaiModel = settings.openaiModel;
    anthropicModel = settings.anthropicModel;
    customBaseUrl = settings.customBaseUrl;
    customModel = settings.customModel;
  });

  $effect(() => {
    if (errorHideTimer) clearTimeout(errorHideTimer);
    const currentError = error;
    if (currentError) {
      errorHideTimer = setTimeout(() => {
        if (error === currentError) error = "";
      }, 6000);
    }
  });

  onMount(() => {
    (async () => {
      try {
        const [openai, anthropic, custom] = await Promise.all([
          credLoad(CRED_KEYS.openai),
          credLoad(CRED_KEYS.anthropic),
          credLoad(CRED_KEYS.custom),
        ]);
        openaiApiKey = openai?.password ?? "";
        anthropicApiKey = anthropic?.password ?? "";
        customApiKey = custom?.password ?? "";
      } catch (err) {
        error = err instanceof Error ? err.message : String(err);
      } finally {
        loadingKeys = false;
      }
    })();
  });

  onDestroy(() => {
    if (errorHideTimer) clearTimeout(errorHideTimer);
  });

  async function persistKey(target: CloudProvider, value: string) {
    const key = CRED_KEYS[target];
    const trimmed = value.trim();
    if (trimmed) {
      await credSave(key, "api-key", trimmed);
    } else {
      await credDelete(key);
    }
  }

  async function handleSave() {
    saving = true;
    error = "";
    try {
      await Promise.all([
        persistKey("openai", openaiApiKey),
        persistKey("anthropic", anthropicApiKey),
        persistKey("custom", customApiKey),
      ]);
      onSave({
        provider,
        localModelId,
        localProfile,
        openaiModel: openaiModel.trim() || "gpt-4o-mini",
        anthropicModel: anthropicModel.trim() || "claude-3-5-haiku-latest",
        customBaseUrl: customBaseUrl.trim(),
        customModel: customModel.trim(),
      });
    } catch (err) {
      error = err instanceof Error ? err.message : String(err);
    } finally {
      saving = false;
    }
  }

  function formatSize(mb: number): string {
    return mb >= 1000 ? `${(mb / 1000).toFixed(1)} GB` : `${mb} MB`;
  }

  function recommendedModelForProfile(profile: CommitAiLocalProfile): string {
    if (profile === "balanced") return "qwen2.5-1.5b";
    if (profile === "detailed") return "qwen2.5-3b";
    return "qwen2.5-0.5b";
  }

  function selectLocalProfile(profile: CommitAiLocalProfile) {
    const previousRecommended = recommendedModelForProfile(localProfile);
    localProfile = profile;
    const nextRecommended = recommendedModelForProfile(profile);
    if (!localModelId || localModelId === previousRecommended) {
      localModelId = nextRecommended;
    }
  }

  let selectedLocalModel = $derived(localModels.find((option) => option.id === localModelId));
</script>

<div
  class="dialog-backdrop"
  role="presentation"
>
  <div class="dialog ai-settings-dialog" role="dialog" aria-modal="true" aria-label="AI settings" tabindex="-1">
    <header class="dialog-header">
      <div>
        <span class="eyebrow">Commit AI</span>
        <h2 class="mt-0.5 text-ink text-base font-bold leading-tight">AI settings</h2>
      </div>
      <button class="dialog-close" type="button" onclick={onClose} title="Close">
        <X size={18} aria-hidden="true" />
      </button>
    </header>

    <form class="ai-settings-form" onsubmit={(e) => { e.preventDefault(); void handleSave(); }}>
      <div class="ai-provider-options" role="radiogroup" aria-label="AI provider">
        <button
          type="button"
          class="ai-provider-option ai-provider-option-local"
          class:active={provider === "local"}
          disabled
          title="Local AI is still in development and not yet available"
        >
          <Cpu size={16} aria-hidden="true" />
          Local AI
          <span class="ai-provider-badge">In development</span>
        </button>
        <button type="button" class="ai-provider-option" class:active={provider === "openai"} onclick={() => { provider = "openai"; }}>
          <Bot size={16} aria-hidden="true" />
          OpenAI
        </button>
        <button type="button" class="ai-provider-option" class:active={provider === "anthropic"} onclick={() => { provider = "anthropic"; }}>
          <Bot size={16} aria-hidden="true" />
          Anthropic (Claude)
        </button>
        <button type="button" class="ai-provider-option" class:active={provider === "custom"} onclick={() => { provider = "custom"; }}>
          <Globe size={16} aria-hidden="true" />
          Custom endpoint
        </button>
      </div>

      {#if provider === "local"}
        <div class="cred-field">
          <span class="cred-field-label">Local speed</span>
          <div class="ai-local-profile-options" role="radiogroup" aria-label="Local AI speed">
            <button type="button" class="ai-local-profile-option" class:active={localProfile === "fast"} onclick={() => selectLocalProfile("fast")}>
              <Zap size={15} aria-hidden="true" />
              Fast
            </button>
            <button type="button" class="ai-local-profile-option" class:active={localProfile === "balanced"} onclick={() => selectLocalProfile("balanced")}>
              <Gauge size={15} aria-hidden="true" />
              Balanced
            </button>
            <button type="button" class="ai-local-profile-option" class:active={localProfile === "detailed"} onclick={() => selectLocalProfile("detailed")}>
              <Sparkles size={15} aria-hidden="true" />
              Detailed
            </button>
          </div>
        </div>
        <label class="cred-field">
          <span class="cred-field-label">Model</span>
          <SelectMenu value={localModelId} options={localModels.map((option) => ({ value: option.id, label: `${option.label} - ${formatSize(option.approx_size_mb)}` }))} onChange={(value) => { localModelId = value; }} />
        </label>
        <div class="cred-token-hint">
          <AlertCircle size={13} aria-hidden="true" />
          <span>
            Switching downloads the model{selectedLocalModel ? ` (${formatSize(selectedLocalModel.approx_size_mb)})` : ""}
            in the background — depending on your internet connection this can take several minutes.
            After that it stays cached locally and loads instantly on the next start.
            The speed setting only changes Local AI; API providers keep their existing prompt.
          </span>
        </div>
      {:else if provider === "openai"}
        <label class="cred-field">
          <span class="cred-field-label">Model</span>
          <input type="text" bind:value={openaiModel} placeholder="gpt-4o-mini" autocomplete="off" spellcheck="false" />
        </label>
        <div class="cred-field">
          <span class="cred-field-label">API key</span>
          <div class="cred-input">
            <Key size={15} class="cred-field-icon" aria-hidden="true" />
            <input
              type={showKey ? "text" : "password"}
              bind:value={openaiApiKey}
              placeholder="sk-..."
              autocomplete="off"
              spellcheck="false"
              disabled={loadingKeys}
            />
            <button type="button" class="cred-reveal" tabindex="-1" onclick={() => { showKey = !showKey; }} aria-label={showKey ? "Hide" : "Show"}>
              {#if showKey}<EyeOff size={14} aria-hidden="true" />{:else}<Eye size={14} aria-hidden="true" />{/if}
            </button>
          </div>
        </div>
      {:else if provider === "anthropic"}
        <label class="cred-field">
          <span class="cred-field-label">Model</span>
          <input type="text" bind:value={anthropicModel} placeholder="claude-3-5-haiku-latest" autocomplete="off" spellcheck="false" />
        </label>
        <div class="cred-field">
          <span class="cred-field-label">API key</span>
          <div class="cred-input">
            <Key size={15} class="cred-field-icon" aria-hidden="true" />
            <input
              type={showKey ? "text" : "password"}
              bind:value={anthropicApiKey}
              placeholder="sk-ant-..."
              autocomplete="off"
              spellcheck="false"
              disabled={loadingKeys}
            />
            <button type="button" class="cred-reveal" tabindex="-1" onclick={() => { showKey = !showKey; }} aria-label={showKey ? "Hide" : "Show"}>
              {#if showKey}<EyeOff size={14} aria-hidden="true" />{:else}<Eye size={14} aria-hidden="true" />{/if}
            </button>
          </div>
        </div>
      {:else}
        <label class="cred-field">
          <span class="cred-field-label">Endpoint URL</span>
          <input type="text" bind:value={customBaseUrl} placeholder="http://localhost:11434/v1" autocomplete="off" spellcheck="false" />
        </label>
        <label class="cred-field">
          <span class="cred-field-label">Model</span>
          <input type="text" bind:value={customModel} placeholder="llama3.1" autocomplete="off" spellcheck="false" />
        </label>
        <div class="cred-field">
          <span class="cred-field-label">API key (optional)</span>
          <div class="cred-input">
            <Key size={15} class="cred-field-icon" aria-hidden="true" />
            <input
              type={showKey ? "text" : "password"}
              bind:value={customApiKey}
              placeholder="Optional"
              autocomplete="off"
              spellcheck="false"
              disabled={loadingKeys}
            />
            <button type="button" class="cred-reveal" tabindex="-1" onclick={() => { showKey = !showKey; }} aria-label={showKey ? "Hide" : "Show"}>
              {#if showKey}<EyeOff size={14} aria-hidden="true" />{:else}<Eye size={14} aria-hidden="true" />{/if}
            </button>
          </div>
        </div>
        <div class="cred-token-hint">
          <Globe size={13} aria-hidden="true" />
          <span>For local OpenAI-compatible servers like Ollama or LM Studio. The base URL should end in /v1.</span>
        </div>
      {/if}

      {#if error}
        <p class="commit-block-reason">{error}</p>
      {/if}

      <div class="new-branch-actions">
        <button class="btn-secondary" type="button" onclick={onClose} disabled={saving}>
          Cancel
        </button>
        <button class="btn-primary" type="submit" disabled={saving || loadingKeys}>
          {#if saving}
            <LoaderCircle class="spin" size={16} aria-hidden="true" />
          {:else}
            <Check size={16} aria-hidden="true" />
          {/if}
          Save
        </button>
      </div>
    </form>
  </div>
</div>
