import * as React from "react";
import { useTranslation } from "react-i18next";
import {
  KeyRound,
  Pencil,
  Plus,
  Search,
  Trash2,
  Unplug,
  Wifi,
  WifiOff,
} from "lucide-react";
import {
  createModelProvider,
  deleteModelProvider,
  discoverModels,
  listModelProviders,
  testModelProviderConnection,
  updateModelProvider,
} from "@/api";
import { ApiErrorState } from "@/components/shared/ApiErrorState";
import { EmptyState } from "@/components/shared/EmptyState";
import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
import { MetricCard } from "@/components/shared/MetricCard";
import { PageHeader } from "@/components/shared/PageHeader";
import { StatusBadge } from "@/components/shared/StatusBadge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Dialog } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Select } from "@/components/ui/select";
import { toast } from "@/components/ui/toaster";
import {
  type DiscoveredModel,
  type ModelItem,
  type ModelProvider,
  type ModelProviderType,
  type ModelProviderUpdateRequest,
  type ModelType,
} from "@/types";

const PROVIDER_TYPE_OPTIONS = [
  { value: "openai", label: "OpenAI" },
  { value: "anthropic", label: "Anthropic" },
  { value: "deepseek", label: "DeepSeek" },
  { value: "azure_openai", label: "Azure OpenAI" },
  { value: "ollama", label: "Ollama" },
  { value: "custom", label: "Custom" },
];

const DEFAULT_URLS = {
  openai: "https://api.openai.com/v1",
  anthropic: "https://api.anthropic.com",
  deepseek: "https://api.deepseek.com/v1",
  azure_openai: "https://<resource>.openai.azure.com",
  ollama: "http://localhost:11434",
  custom: "",
} as const;

function ModelTypeBadge({ type, compact }: { type: ModelType; compact?: boolean }) {
  const labels: Record<ModelType, string> = {
    chat: "Chat",
    reasoning: "Reasoning",
    embedding: "Embedding",
    image: "Image",
    audio: "Audio",
    video: "Video",
    rerank: "Rerank",
    moderation: "Moderation",
    other: "Other",
  };
  return (
    <span className={`rounded bg-muted px-1.5 py-0.5 text-xs ${compact ? "" : "text-muted-foreground"}`}>
      {labels[type] || type}
    </span>
  );
}

export function ModelProvidersPage() {
  const { t } = useTranslation();
  const [providers, setProviders] = React.useState<ModelProvider[]>([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState<string>();
  const [search, setSearch] = React.useState("");
  const [providerDialogOpen, setProviderDialogOpen] = React.useState(false);
  const [editingProvider, setEditingProvider] = React.useState<ModelProvider | null>(null);

  const load = React.useCallback(async () => {
    setLoading(true);
    setError(undefined);
    try {
      const provs = await listModelProviders();
      setProviders(provs);
    } catch (err) {
      setError(err instanceof Error ? err.message : t("errors.failedToLoad"));
    } finally {
      setLoading(false);
    }
  }, [t]);

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

  async function removeProvider(id: string) {
    await deleteModelProvider(id);
    toast.success(t("modelProviders.providerDeleted"));
    void load();
  }

  async function toggleProviderStatus(provider: ModelProvider) {
    const next = provider.status === "active" ? "inactive" : "active";
    await updateModelProvider(provider.id, { status: next });
    void load();
  }

  async function testConnection(provider: ModelProvider) {
    try {
      const result = await testModelProviderConnection(provider.id);
      if (result.success) {
        toast.success(t("modelProviders.testSuccess", { latency: result.latency_ms }));
      } else {
        toast.error(`${t("modelProviders.testFailed")}: ${result.message}`);
      }
    } catch {
      toast.error(t("modelProviders.testFailed"));
    }
  }

  const filtered = providers.filter(
    (p) =>
      p.name.toLowerCase().includes(search.toLowerCase()) ||
      p.provider_type.toLowerCase().includes(search.toLowerCase())
  );

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

  return (
    <div className="space-y-6">
      <PageHeader
        title={t("modelProviders.title")}
        description={t("modelProviders.description")}
        actions={
          <Button
            onClick={() => {
              setEditingProvider(null);
              setProviderDialogOpen(true);
            }}
          >
            <Plus className="h-4 w-4" />
            {t("modelProviders.addProvider")}
          </Button>
        }
      />

      <div className="grid gap-4 md:grid-cols-3">
        <MetricCard label={t("modelProviders.totalProviders")} value={providers.length} icon={Unplug} />
        <MetricCard
          label={t("modelProviders.activeProviders")}
          value={providers.filter((p) => p.status === "active").length}
          icon={Wifi}
        />
        <MetricCard
          label={t("modelProviders.totalModels")}
          value={providers.reduce((acc, p) => acc + p.models.filter((m) => m.enabled).length, 0)}
          icon={KeyRound}
        />
      </div>

      <Card>
        <CardHeader>
          <div className="flex items-center justify-between gap-4">
            <CardTitle>{t("modelProviders.providers")}</CardTitle>
            <div className="relative w-64">
              <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
              <Input
                value={search}
                onChange={(e) => setSearch(e.target.value)}
                placeholder={t("modelProviders.searchProviders")}
                className="pl-9"
              />
            </div>
          </div>
        </CardHeader>
        <CardContent>
          {filtered.length ? (
            <div className="space-y-3">
              {filtered.map((provider) => (
                <div
                  key={provider.id}
                  className="flex items-start justify-between gap-4 rounded-lg border border-border p-4"
                >
                  <div className="min-w-0 flex-1 space-y-2">
                    <div className="flex items-center gap-2">
                      <span className="font-medium">{provider.name}</span>
                      <StatusBadge status={provider.status} />
                      <span className="rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
                        {t(`modelProviders.${provider.provider_type}`)}
                      </span>
                    </div>
                    <p className="truncate text-xs text-muted-foreground">{provider.base_url}</p>
                    <div className="flex flex-wrap gap-1">
                      {provider.models.filter((m) => m.enabled).map((m) => (
                        <span
                          key={m.model_id}
                          className="flex items-center gap-1 rounded bg-primary/10 px-1.5 py-0.5 text-xs text-primary"
                        >
                          {m.display_name}
                          {provider.default_model === m.model_id ? " *" : ""}
                          <ModelTypeBadge type={m.model_type} compact />
                        </span>
                      ))}
                      {provider.models.filter((m) => m.enabled).length === 0 && (
                        <span className="text-xs text-muted-foreground">{t("modelProviders.noModels")}</span>
                      )}
                    </div>
                  </div>
                  <div className="flex shrink-0 items-center gap-1">
                    <Button size="sm" variant="ghost" onClick={() => void testConnection(provider)} title={t("modelProviders.testConnection")}>
                      <Wifi className="h-4 w-4" />
                    </Button>
                    <Button
                      size="sm"
                      variant="ghost"
                      onClick={() => void toggleProviderStatus(provider)}
                      title={t("modelProviders.toggleStatus")}
                    >
                      {provider.status === "active" ? <WifiOff className="h-4 w-4" /> : <Wifi className="h-4 w-4" />}
                    </Button>
                    <Button
                      size="sm"
                      variant="ghost"
                      onClick={() => {
                        setEditingProvider(provider);
                        setProviderDialogOpen(true);
                      }}
                      title={t("modelProviders.editProvider")}
                    >
                      <Pencil className="h-4 w-4" />
                    </Button>
                    <Button
                      size="sm"
                      variant="ghost"
                      onClick={() => {
                        if (window.confirm(t("modelProviders.deleteConfirm"))) {
                          void removeProvider(provider.id);
                        }
                      }}
                      title={t("modelProviders.deleteProvider")}
                    >
                      <Trash2 className="h-4 w-4 text-destructive" />
                    </Button>
                  </div>
                </div>
              ))}
            </div>
          ) : (
            <EmptyState
              icon={Unplug}
              title={t("modelProviders.noProviders")}
              description={t("modelProviders.noProvidersDescription")}
            />
          )}
        </CardContent>
      </Card>

      <ProviderDialog
        open={providerDialogOpen}
        onOpenChange={setProviderDialogOpen}
        editing={editingProvider}
        onSaved={() => void load()}
      />
    </div>
  );
}

function ProviderDialog({
  open,
  onOpenChange,
  editing,
  onSaved,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  editing: ModelProvider | null;
  onSaved: () => void;
}) {
  const { t } = useTranslation();
  const [name, setName] = React.useState("");
  const [providerType, setProviderType] = React.useState<ModelProviderType>("openai");
  const [baseUrl, setBaseUrl] = React.useState("");
  const [apiKey, setApiKey] = React.useState("");
  const [models, setModels] = React.useState<ModelItem[]>([]);
  const [defaultModel, setDefaultModel] = React.useState("");
  const [submitting, setSubmitting] = React.useState(false);
  const [discovering, setDiscovering] = React.useState(false);
  const [discovered, setDiscovered] = React.useState<DiscoveredModel[]>([]);
  const [discoverOpen, setDiscoverOpen] = React.useState(false);
  const [selectedDiscovered, setSelectedDiscovered] = React.useState<Set<string>>(new Set());
  const [typeFilter, setTypeFilter] = React.useState<string>("all");

  React.useEffect(() => {
    if (!open) {
      setDiscoverOpen(false);
      setDiscovered([]);
      setSelectedDiscovered(new Set());
      return;
    }
    if (editing) {
      setName(editing.name);
      setProviderType(editing.provider_type);
      setBaseUrl(editing.base_url);
      setApiKey("");
      setModels(editing.models.map((m) => ({ ...m })));
      setDefaultModel(editing.default_model ?? "");
    } else {
      setName("");
      setProviderType("openai");
      setBaseUrl(DEFAULT_URLS.openai);
      setApiKey("");
      setModels([]);
      setDefaultModel("");
    }
  }, [open, editing]);

  function handleTypeChange(newType: string) {
    const pt = newType as ModelProviderType;
    setProviderType(pt);
    setBaseUrl(DEFAULT_URLS[pt] ?? "");
    setModels([]);
    setDefaultModel("");
    setDiscovered([]);
    setDiscoverOpen(false);
  }

  function updateModel(index: number, patch: Partial<ModelItem>) {
    setModels((prev) => prev.map((m, i) => (i === index ? { ...m, ...patch } : m)));
  }

  function addModelRow() {
    setModels((prev) => [...prev, { model_id: "", display_name: "", model_type: "chat", enabled: true }]);
  }

  function removeModelRow(index: number) {
    setModels((prev) => prev.filter((_, i) => i !== index));
  }

  async function handleDiscover() {
    setDiscovering(true);
    setDiscovered([]);
    setSelectedDiscovered(new Set());
    try {
      const result = await discoverModels(
        editing ? { providerId: editing.id } : { providerType, baseUrl, apiKey },
      );
      setDiscovered(result.models);
      setDiscoverOpen(true);
      toast.success(t("modelProviders.discovered", { count: result.models.length }));
    } catch {
      toast.error(t("modelProviders.discoverFailed"));
    } finally {
      setDiscovering(false);
    }
  }

  function toggleDiscovered(modelId: string) {
    setSelectedDiscovered((prev) => {
      const next = new Set(prev);
      if (next.has(modelId)) next.delete(modelId);
      else next.add(modelId);
      return next;
    });
  }

  function toggleAllDiscovered() {
    const filteredDiscovered = typeFilter === "all" ? discovered : discovered.filter((m) => m.model_type === typeFilter);
    if (selectedDiscovered.size === filteredDiscovered.length) {
      setSelectedDiscovered(new Set());
    } else {
      setSelectedDiscovered(new Set(filteredDiscovered.map((m) => m.model_id)));
    }
  }

  const filteredDiscovered = typeFilter === "all" ? discovered : discovered.filter((m) => m.model_type === typeFilter);

  function applyDiscovered() {
    const picked = discovered.filter((m) => selectedDiscovered.has(m.model_id));
    const existingIds = new Set(models.map((m) => m.model_id));
    const newItems: ModelItem[] = picked
      .filter((m) => !existingIds.has(m.model_id))
      .map((m) => ({
        model_id: m.model_id,
        display_name: m.display_name,
        model_type: m.model_type,
        enabled: true,
      }));
    setModels((prev) => [...prev, ...newItems]);
    if (!defaultModel && newItems.length > 0) {
      setDefaultModel(newItems[0]!.model_id);
    }
    setDiscoverOpen(false);
    setDiscovered([]);
    setSelectedDiscovered(new Set());
  }

  async function submit(event: React.FormEvent) {
    event.preventDefault();
    setSubmitting(true);
    try {
      const validModels = models.filter((m) => m.model_id.trim());
      if (editing) {
        const patch: ModelProviderUpdateRequest = { name, base_url: baseUrl, models: validModels, default_model: defaultModel || null };
        if (apiKey) patch.api_key = apiKey;
        await updateModelProvider(editing.id, patch);
        toast.success(t("modelProviders.providerUpdated"));
      } else {
        await createModelProvider({
          name,
          provider_type: providerType,
          base_url: baseUrl,
          api_key: apiKey,
          models: validModels,
          default_model: defaultModel || null,
        });
        toast.success(t("modelProviders.providerCreated"));
      }
      onOpenChange(false);
      onSaved();
    } catch {
      toast.error(t("errors.failedToSave"));
    } finally {
      setSubmitting(false);
    }
  }

  const discoveredTypes = Array.from(new Set(discovered.map((m) => m.model_type)));

  return (
    <Dialog
      open={open}
      onOpenChange={onOpenChange}
      title={editing ? t("modelProviders.editProvider") : t("modelProviders.createProvider")}
      className="max-w-4xl"
    >
      <form className="space-y-4" onSubmit={submit}>
        <div className="grid gap-4 sm:grid-cols-2">
          <label className="block space-y-2 text-sm">
            <span className="text-muted-foreground">{t("modelProviders.providerName")}</span>
            <Input required value={name} onChange={(e) => setName(e.target.value)} />
          </label>
          <label className="block space-y-2 text-sm">
            <span className="text-muted-foreground">{t("modelProviders.providerType")}</span>
            <Select options={PROVIDER_TYPE_OPTIONS} value={providerType} onChange={(e) => handleTypeChange(e.target.value)} disabled={!!editing} />
          </label>
        </div>
        <div className="grid gap-4 sm:grid-cols-2">
          <label className="block space-y-2 text-sm">
            <span className="text-muted-foreground">{t("modelProviders.baseUrl")}</span>
            <Input required value={baseUrl} onChange={(e) => setBaseUrl(e.target.value)} />
          </label>
          <label className="block space-y-2 text-sm">
            <span className="text-muted-foreground">{t("modelProviders.apiKey")}</span>
            <Input type="password" value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder={editing ? t("modelProviders.masked") : "sk-..."} />
          </label>
        </div>

        <div className="space-y-2">
          <div className="flex items-center justify-between">
            <span className="text-sm font-medium">{t("modelProviders.availableModels")} ({models.length})</span>
            <div className="flex gap-2">
              <Button type="button" size="sm" variant="outline" onClick={() => void handleDiscover()} disabled={discovering}>
                {discovering ? t("modelProviders.discovering") : t("modelProviders.discoverModels")}
              </Button>
              <Button type="button" size="sm" variant="outline" onClick={addModelRow}>
                {t("modelProviders.addModel")}
              </Button>
            </div>
          </div>
          {models.length > 0 && (
            <div className="max-h-48 space-y-2 overflow-auto rounded border p-2">
              {models.map((model, index) => (
                <div key={index} className="flex items-center gap-2">
                  <Input
                    placeholder={t("modelProviders.modelId")}
                    value={model.model_id}
                    onChange={(e) => updateModel(index, { model_id: e.target.value })}
                    className="flex-1"
                  />
                  <Input
                    placeholder={t("modelProviders.displayName")}
                    value={model.display_name}
                    onChange={(e) => updateModel(index, { display_name: e.target.value })}
                    className="w-32"
                  />
                  <Select
                    value={model.model_type}
                    onChange={(e) => updateModel(index, { model_type: e.target.value as ModelType })}
                    options={[
                      { value: "chat", label: "Chat" },
                      { value: "reasoning", label: "Reasoning" },
                      { value: "embedding", label: "Embedding" },
                      { value: "rerank", label: "Rerank" },
                    ]}
                    className="w-28"
                  />
                  <Button type="button" size="icon" variant="ghost" onClick={() => removeModelRow(index)}>
                    <Trash2 className="h-4 w-4" />
                  </Button>
                </div>
              ))}
            </div>
          )}
        </div>

        {discoverOpen && (
          <div className="space-y-2 rounded border p-4">
            <div className="flex items-center justify-between">
              <span className="text-sm font-medium">{t("modelProviders.discoveredModels")}</span>
              <div className="flex gap-2">
                <Select
                  value={typeFilter}
                  onChange={(e) => setTypeFilter(e.target.value)}
                  options={[{ value: "all", label: t("modelProviders.allTypes") }, ...discoveredTypes.map((t) => ({ value: t, label: t }))]}
                  className="w-32"
                />
                <Button type="button" size="sm" variant="outline" onClick={toggleAllDiscovered}>
                  {selectedDiscovered.size === filteredDiscovered.length ? t("modelProviders.deselectAll") : t("modelProviders.selectAll")}
                </Button>
              </div>
            </div>
            <div className="max-h-60 space-y-1 overflow-auto">
              {filteredDiscovered.map((m) => (
                <button
                  key={m.model_id}
                  type="button"
                  onClick={() => toggleDiscovered(m.model_id)}
                  className={`flex w-full items-center gap-2 rounded p-2 text-left transition ${selectedDiscovered.has(m.model_id) ? "bg-primary/10" : "hover:bg-muted"}`}
                >
                  <div className={`h-4 w-4 rounded border ${selectedDiscovered.has(m.model_id) ? "border-primary bg-primary" : "border-muted-foreground"}`} />
                  <div className="flex-1">
                    <span className="text-sm">{m.display_name}</span>
                    <span className="ml-2 text-xs text-muted-foreground">{m.model_id}</span>
                  </div>
                  <ModelTypeBadge type={m.model_type} />
                  {m.context_window && (
                    <span className="text-xs text-muted-foreground">{m.context_window.toLocaleString()} tokens</span>
                  )}
                </button>
              ))}
              {filteredDiscovered.length === 0 && (
                <p className="py-4 text-center text-sm text-muted-foreground">{t("modelProviders.noModelsDiscovered")}</p>
              )}
            </div>
            <div className="flex justify-end gap-2">
              <Button type="button" variant="ghost" onClick={() => setDiscoverOpen(false)}>
                {t("common.cancel")}
              </Button>
              <Button type="button" onClick={applyDiscovered} disabled={selectedDiscovered.size === 0}>
                {t("modelProviders.applySelected", { count: selectedDiscovered.size })}
              </Button>
            </div>
          </div>
        )}

        <div className="flex justify-end gap-2 pt-2">
          <Button type="button" variant="ghost" onClick={() => onOpenChange(false)}>
            {t("common.cancel")}
          </Button>
          <Button type="submit" disabled={submitting}>
            {submitting ? t("common.creating") : t("common.save")}
          </Button>
        </div>
      </form>
    </Dialog>
  );
}
