import { useEffect, useMemo, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { ChevronDown, ChevronRight, Loader2, Plus, Trash2 } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { BasicFormFields } from "./BasicFormFields";
import { providerSchema, type ProviderFormData } from "@/lib/schemas/provider";
import type {
  ClaudeApiFormat,
  ClaudeDesktopModelRoute,
  ProviderCategory,
  ProviderMeta,
} from "@/types";
import type { OpenClawSuggestedDefaults } from "@/config/openclawProviderPresets";
import {
  fetchModelsForConfig,
  showFetchModelsError,
  type FetchedModel,
} from "@/lib/api/model-fetch";
import {
  providersApi,
  type ClaudeDesktopDefaultRoute,
} from "@/lib/api/providers";

export type ClaudeDesktopProviderFormValues = ProviderFormData & {
  presetId?: string;
  presetCategory?: ProviderCategory;
  isPartner?: boolean;
  meta?: ProviderMeta;
  providerKey?: string;
  suggestedDefaults?: OpenClawSuggestedDefaults;
};

export interface ClaudeDesktopProviderFormProps {
  submitLabel: string;
  onSubmit: (values: ClaudeDesktopProviderFormValues) => Promise<void> | void;
  onCancel: () => void;
  onSubmittingChange?: (isSubmitting: boolean) => void;
  initialData?: {
    name?: string;
    websiteUrl?: string;
    notes?: string;
    settingsConfig?: Record<string, unknown>;
    category?: ProviderCategory;
    meta?: ProviderMeta;
    icon?: string;
    iconColor?: string;
  };
  showButtons?: boolean;
}

type RouteRow = {
  route: string;
  model: string;
  displayName: string;
  supports1m: boolean;
};

function envString(
  settingsConfig: Record<string, unknown> | undefined,
  key: string,
) {
  const env = settingsConfig?.env;
  if (!env || typeof env !== "object") return "";
  const value = (env as Record<string, unknown>)[key];
  return typeof value === "string" ? value : "";
}

function clonePlainRecord(value: unknown): Record<string, unknown> {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return {};
  }
  return { ...(value as Record<string, unknown>) };
}

function initialRouteRows(
  routes: Record<string, ClaudeDesktopModelRoute> | undefined,
): RouteRow[] {
  return Object.entries(routes ?? {}).map(([route, value]) => ({
    route,
    model: value.model ?? "",
    displayName: value.displayName ?? "",
    supports1m: value.supports1m ?? false,
  }));
}

function isClaudeSafeRoute(route: string) {
  const normalized = route.trim().toLowerCase();
  return (
    normalized.startsWith("claude-") ||
    normalized.startsWith("anthropic/claude-")
  );
}

function defaultRouteRows(
  defaults: ClaudeDesktopDefaultRoute[],
  defaultModel: string,
): RouteRow[] {
  return defaults.map((route, index) => ({
    route: route.routeId,
    model: index === 0 ? defaultModel : "",
    displayName: route.displayName,
    supports1m: route.supports1m,
  }));
}

function nextRouteRow(current: RouteRow[], defaults: RouteRow[]): RouteRow {
  return (
    defaults.find(
      (route) => !current.some((existing) => existing.route === route.route),
    ) ?? {
      route: "",
      model: "",
      displayName: "",
      supports1m: true,
    }
  );
}

export function ClaudeDesktopProviderForm({
  submitLabel,
  onSubmit,
  onCancel,
  onSubmittingChange,
  initialData,
  showButtons = true,
}: ClaudeDesktopProviderFormProps) {
  const { t } = useTranslation();
  const initialMode = initialData?.meta?.claudeDesktopMode ?? "direct";
  const [mode, setMode] = useState<"direct" | "proxy">(initialMode);
  const needsModelMapping = mode === "proxy";
  const [apiFormat, setApiFormat] = useState<ClaudeApiFormat>(
    initialData?.meta?.apiFormat ?? "anthropic",
  );
  const [baseUrl, setBaseUrl] = useState(
    envString(initialData?.settingsConfig, "ANTHROPIC_BASE_URL"),
  );
  const [apiKey, setApiKey] = useState(
    envString(initialData?.settingsConfig, "ANTHROPIC_AUTH_TOKEN") ||
      envString(initialData?.settingsConfig, "ANTHROPIC_API_KEY"),
  );
  const [routes, setRoutes] = useState<RouteRow[]>(() =>
    initialRouteRows(initialData?.meta?.claudeDesktopModelRoutes),
  );
  const didSeedDefaultRoutes = useRef(
    Object.keys(initialData?.meta?.claudeDesktopModelRoutes ?? {}).length > 0,
  );
  const [fetchedModels, setFetchedModels] = useState<FetchedModel[]>([]);
  const [isFetchingModels, setIsFetchingModels] = useState(false);
  const [directModelsExpanded, setDirectModelsExpanded] = useState(
    initialMode === "direct" &&
      Object.keys(initialData?.meta?.claudeDesktopModelRoutes ?? {}).length > 0,
  );
  const { data: defaultRoutes = [] } = useQuery({
    queryKey: ["claudeDesktopDefaultRoutes"],
    queryFn: () => providersApi.getClaudeDesktopDefaultRoutes(),
  });
  const defaultProxyRouteRows = useMemo(
    () =>
      defaultRouteRows(
        defaultRoutes,
        envString(initialData?.settingsConfig, "ANTHROPIC_MODEL"),
      ),
    [defaultRoutes, initialData?.settingsConfig],
  );

  const defaultValues: ProviderFormData = useMemo(
    () => ({
      name: initialData?.name ?? "",
      websiteUrl: initialData?.websiteUrl ?? "",
      notes: initialData?.notes ?? "",
      settingsConfig: JSON.stringify(
        initialData?.settingsConfig ?? { env: {} },
        null,
        2,
      ),
      icon: initialData?.icon ?? "",
      iconColor: initialData?.iconColor ?? "",
    }),
    [initialData],
  );

  const form = useForm<ProviderFormData>({
    resolver: zodResolver(providerSchema),
    defaultValues,
    mode: "onSubmit",
  });

  useEffect(() => {
    onSubmittingChange?.(form.formState.isSubmitting || isFetchingModels);
  }, [form.formState.isSubmitting, isFetchingModels, onSubmittingChange]);

  const updateRoute = (index: number, patch: Partial<RouteRow>) => {
    setRoutes((current) =>
      current.map((row, i) => (i === index ? { ...row, ...patch } : row)),
    );
  };

  const handleModelMappingChange = (checked: boolean) => {
    setMode(checked ? "proxy" : "direct");
    if (checked) {
      setRoutes((current) => {
        if (current.length > 0 || defaultProxyRouteRows.length === 0) {
          return current;
        }
        didSeedDefaultRoutes.current = true;
        return defaultProxyRouteRows;
      });
    }
  };

  useEffect(() => {
    if (
      didSeedDefaultRoutes.current ||
      mode !== "proxy" ||
      routes.length > 0 ||
      defaultProxyRouteRows.length === 0
    ) {
      return;
    }

    didSeedDefaultRoutes.current = true;
    setRoutes(defaultProxyRouteRows);
  }, [defaultProxyRouteRows, mode, routes.length]);

  const handleFetchModels = async () => {
    if (!baseUrl.trim() || !apiKey.trim()) {
      showFetchModelsError(null, t, {
        hasBaseUrl: Boolean(baseUrl.trim()),
        hasApiKey: Boolean(apiKey.trim()),
      });
      return;
    }

    setIsFetchingModels(true);
    try {
      const models = await fetchModelsForConfig(baseUrl.trim(), apiKey.trim());
      setFetchedModels(models);
      toast.success(
        t("providerForm.fetchModelsSuccess", {
          count: models.length,
          defaultValue: `已获取 ${models.length} 个模型`,
        }),
      );
    } catch (error) {
      showFetchModelsError(error, t, {
        hasBaseUrl: Boolean(baseUrl.trim()),
        hasApiKey: Boolean(apiKey.trim()),
      });
    } finally {
      setIsFetchingModels(false);
    }
  };

  const handleSubmit = async (values: ProviderFormData) => {
    if (!values.name.trim()) {
      toast.error(
        t("providerForm.fillSupplierName", {
          defaultValue: "请填写供应商名称",
        }),
      );
      return;
    }
    if (!baseUrl.trim()) {
      toast.error(
        t("providerForm.fetchModelsNeedEndpoint", {
          defaultValue: "请先填写接口地址",
        }),
      );
      return;
    }
    if (!apiKey.trim()) {
      toast.error(
        t("providerForm.fetchModelsNeedApiKey", {
          defaultValue: "请先填写 API Key",
        }),
      );
      return;
    }

    const routeEntries = routes
      .map((route) => ({
        ...route,
        route: route.route.trim(),
        model: route.model.trim(),
        displayName: route.displayName.trim(),
      }))
      .filter((route) => route.route || route.model);

    if (mode === "proxy") {
      const invalid = routeEntries.find(
        (route) =>
          !route.route || !route.model || !isClaudeSafeRoute(route.route),
      );
      if (invalid) {
        toast.error(
          t("claudeDesktop.routeInvalid", {
            defaultValue:
              "模型映射必须填写 claude-* / anthropic/claude-* 路由名和上游模型名",
          }),
        );
        return;
      }
      if (routeEntries.length === 0) {
        toast.error(
          t("claudeDesktop.routesRequired", {
            defaultValue: "需要模型映射时至少填写一个模型路由",
          }),
        );
        return;
      }
    } else {
      const invalid = routeEntries.find(
        (route) => !route.route || !isClaudeSafeRoute(route.route),
      );
      if (invalid) {
        toast.error(
          t("claudeDesktop.directModelInvalid", {
            defaultValue:
              "直连模型必须使用 claude-* / anthropic/claude-* 模型名",
          }),
        );
        return;
      }
    }

    const settingsConfig = clonePlainRecord(initialData?.settingsConfig);
    const env = clonePlainRecord(settingsConfig.env);
    settingsConfig.env = {
      ...env,
      ANTHROPIC_BASE_URL: baseUrl.trim().replace(/\/+$/, ""),
      ANTHROPIC_AUTH_TOKEN: apiKey.trim(),
    };

    const routeMap = routeEntries.reduce<
      Record<string, ClaudeDesktopModelRoute>
    >((acc, route) => {
      acc[route.route] = {
        model: route.model || route.route,
        displayName: route.displayName || undefined,
        supports1m: route.supports1m || undefined,
      };
      return acc;
    }, {});

    const meta: ProviderMeta = {
      ...(initialData?.meta ?? {}),
      claudeDesktopMode: mode,
      apiFormat: mode === "proxy" ? apiFormat : "anthropic",
    };

    meta.claudeDesktopModelRoutes = routeMap;

    delete meta.endpointAutoSelect;
    delete meta.providerType;
    delete meta.isFullUrl;

    await onSubmit({
      ...values,
      name: values.name.trim(),
      websiteUrl: values.websiteUrl?.trim() ?? "",
      notes: values.notes?.trim() ?? "",
      settingsConfig: JSON.stringify(settingsConfig, null, 2),
      meta,
    });
  };

  return (
    <Form {...form}>
      <form
        id="provider-form"
        onSubmit={form.handleSubmit(handleSubmit)}
        className="space-y-6"
      >
        <BasicFormFields form={form} />

        <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
          <div className="space-y-2">
            <Label>
              {t("claudeDesktop.gatewayBaseUrl", {
                defaultValue: "Gateway Base URL",
              })}
            </Label>
            <Input
              value={baseUrl}
              onChange={(event) => setBaseUrl(event.target.value)}
              placeholder="https://api.example.com"
            />
          </div>
          <div className="space-y-2">
            <Label>
              {t("claudeDesktop.bearerToken", {
                defaultValue: "Bearer Token",
              })}
            </Label>
            <Input
              value={apiKey}
              onChange={(event) => setApiKey(event.target.value)}
              type="password"
              placeholder="sk-..."
            />
          </div>
        </div>

        <div className="space-y-3 rounded-lg border border-border-default bg-muted/20 p-4">
          <div className="flex items-center justify-between gap-4">
            <div className="space-y-1">
              <Label>
                {t("claudeDesktop.modelMappingToggle", {
                  defaultValue: "需要模型映射",
                })}
              </Label>
              <p className="text-xs leading-relaxed text-muted-foreground">
                {needsModelMapping
                  ? t("claudeDesktop.modelMappingOnHint", {
                      defaultValue:
                        "Claude Desktop 只看到 claude-* 路由名，CC Switch 本地路由会映射到真实上游模型；需要保持本地路由运行。",
                    })
                  : t("claudeDesktop.modelMappingOffHint", {
                      defaultValue:
                        "适合供应商已经暴露并接受 claude-* / anthropic/claude-* 模型名的 Anthropic Messages API；请求会由 Claude Desktop 直连供应商。",
                    })}
              </p>
            </div>
            <Switch
              checked={needsModelMapping}
              onCheckedChange={handleModelMappingChange}
              aria-label={t("claudeDesktop.modelMappingToggle", {
                defaultValue: "需要模型映射",
              })}
            />
          </div>
        </div>

        {needsModelMapping && (
          <div className="space-y-4 rounded-lg border border-border-default p-4">
            <div className="grid grid-cols-1 gap-4 md:grid-cols-[220px_1fr]">
              <div className="space-y-2">
                <Label>
                  {t("providerForm.apiFormat", { defaultValue: "API 格式" })}
                </Label>
                <Select
                  value={apiFormat}
                  onValueChange={(value) =>
                    setApiFormat(value as ClaudeApiFormat)
                  }
                >
                  <SelectTrigger>
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="anthropic">
                      {t("providerForm.apiFormatAnthropic", {
                        defaultValue: "Anthropic Messages (原生)",
                      })}
                    </SelectItem>
                    <SelectItem value="openai_chat">
                      {t("providerForm.apiFormatOpenAIChat", {
                        defaultValue: "OpenAI Chat Completions (需开启路由)",
                      })}
                    </SelectItem>
                    <SelectItem value="openai_responses">
                      {t("providerForm.apiFormatOpenAIResponses", {
                        defaultValue: "OpenAI Responses API (需开启路由)",
                      })}
                    </SelectItem>
                    <SelectItem value="gemini_native">
                      {t("providerForm.apiFormatGeminiNative", {
                        defaultValue:
                          "Gemini Native generateContent (需开启路由)",
                      })}
                    </SelectItem>
                  </SelectContent>
                </Select>
              </div>
              <div className="flex items-end justify-end">
                <Button
                  type="button"
                  variant="outline"
                  onClick={handleFetchModels}
                  disabled={isFetchingModels}
                >
                  {isFetchingModels ? (
                    <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                  ) : null}
                  {t("providerForm.fetchModels", {
                    defaultValue: "获取模型",
                  })}
                </Button>
              </div>
            </div>

            <datalist id="claude-desktop-upstream-models">
              {fetchedModels.map((model) => (
                <option key={model.id} value={model.id} />
              ))}
            </datalist>

            <div className="space-y-3">
              <div className="space-y-1 border-t border-border-default pt-4">
                <div className="flex items-center justify-between">
                  <Label>
                    {t("claudeDesktop.routeMapTitle", {
                      defaultValue: "模型映射",
                    })}
                  </Label>
                </div>
                <p className="text-xs leading-relaxed text-muted-foreground">
                  {t("claudeDesktop.routeMapHint", {
                    defaultValue:
                      "左侧是 Claude Desktop 看到的 claude-* 模型名，右侧是真实发送给供应商的上游模型名。",
                  })}
                </p>
              </div>

              <div className="hidden grid-cols-[1fr_1fr_140px_92px_36px] gap-2 px-1 text-xs font-medium text-muted-foreground md:grid">
                <span>
                  {t("claudeDesktop.routeModelLabel", {
                    defaultValue: "Desktop 模型",
                  })}
                </span>
                <span>
                  {t("claudeDesktop.upstreamModelLabel", {
                    defaultValue: "上游模型",
                  })}
                </span>
                <span>
                  {t("claudeDesktop.displayNameLabel", {
                    defaultValue: "显示名",
                  })}
                </span>
                <span>
                  {t("claudeDesktop.supports1mLabel", {
                    defaultValue: "1M",
                  })}
                </span>
                <span />
              </div>
              {routes.map((route, index) => (
                <div
                  key={`${route.route}-${index}`}
                  className="grid grid-cols-1 gap-2 md:grid-cols-[1fr_1fr_140px_92px_36px]"
                >
                  <Input
                    value={route.route}
                    onChange={(event) =>
                      updateRoute(index, { route: event.target.value })
                    }
                    placeholder="claude-sonnet-4-6"
                  />
                  <Input
                    value={route.model}
                    onChange={(event) =>
                      updateRoute(index, { model: event.target.value })
                    }
                    list="claude-desktop-upstream-models"
                    placeholder="kimi-k2 / deepseek-chat"
                  />
                  <Input
                    value={route.displayName}
                    onChange={(event) =>
                      updateRoute(index, { displayName: event.target.value })
                    }
                    placeholder="Sonnet"
                  />
                  <label className="flex h-9 items-center gap-2 text-sm text-muted-foreground">
                    <Checkbox
                      checked={route.supports1m}
                      onCheckedChange={(checked) =>
                        updateRoute(index, { supports1m: checked === true })
                      }
                    />
                    1M
                  </label>
                  <Button
                    type="button"
                    variant="ghost"
                    size="icon"
                    onClick={() =>
                      setRoutes((current) =>
                        current.filter((_, i) => i !== index),
                      )
                    }
                  >
                    <Trash2 className="h-4 w-4" />
                  </Button>
                </div>
              ))}
            </div>

            <Button
              type="button"
              variant="outline"
              onClick={() =>
                setRoutes((current) => [
                  ...current,
                  nextRouteRow(current, defaultProxyRouteRows),
                ])
              }
            >
              <Plus className="mr-2 h-4 w-4" />
              {t("claudeDesktop.addRoute", { defaultValue: "添加路由" })}
            </Button>
          </div>
        )}

        {!needsModelMapping && (
          <Collapsible
            open={directModelsExpanded}
            onOpenChange={setDirectModelsExpanded}
          >
            <CollapsibleTrigger asChild>
              <Button
                type="button"
                variant={null}
                size="sm"
                className="h-8 gap-1.5 px-0 text-sm font-medium text-foreground hover:opacity-70"
              >
                {directModelsExpanded ? (
                  <ChevronDown className="h-4 w-4" />
                ) : (
                  <ChevronRight className="h-4 w-4" />
                )}
                {t("claudeDesktop.directModelListTitle", {
                  defaultValue:
                    "手动指定 Claude Desktop 模型列表（高级，可选）",
                })}
              </Button>
            </CollapsibleTrigger>
            {!directModelsExpanded && (
              <p className="ml-1 mt-1 text-xs text-muted-foreground">
                {t("claudeDesktop.directModelListCollapsedHint", {
                  defaultValue:
                    "原生 Claude 模型供应商通常不用填写，Claude Desktop 会自动读取 /v1/models。",
                })}
              </p>
            )}
            <CollapsibleContent className="space-y-4 pt-2">
              <div className="space-y-4 rounded-lg border border-border-default p-4">
                <div className="flex flex-wrap items-start justify-between gap-3">
                  <p className="text-xs leading-relaxed text-muted-foreground">
                    {t("claudeDesktop.directModelListHint", {
                      defaultValue:
                        "仅当供应商的 /v1/models 不可用或没有返回 Claude Desktop 可识别的 claude-* 模型名时填写；这些模型名会原样发送给供应商。",
                    })}
                  </p>
                  <Button
                    type="button"
                    variant="outline"
                    onClick={handleFetchModels}
                    disabled={isFetchingModels}
                  >
                    {isFetchingModels ? (
                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                    ) : null}
                    {t("providerForm.fetchModels", {
                      defaultValue: "获取模型",
                    })}
                  </Button>
                </div>

                <datalist id="claude-desktop-direct-models">
                  {fetchedModels.map((model) => (
                    <option key={model.id} value={model.id} />
                  ))}
                </datalist>

                {routes.length > 0 ? (
                  <div className="space-y-2">
                    {routes.map((route, index) => (
                      <div
                        key={`${route.route}-${index}`}
                        className="grid grid-cols-1 gap-2 md:grid-cols-[1fr_92px_36px]"
                      >
                        <Input
                          value={route.route}
                          onChange={(event) =>
                            updateRoute(index, { route: event.target.value })
                          }
                          list="claude-desktop-direct-models"
                          placeholder="claude-deepseek-chat"
                        />
                        <label className="flex h-9 items-center gap-2 text-sm text-muted-foreground">
                          <Checkbox
                            checked={route.supports1m}
                            onCheckedChange={(checked) =>
                              updateRoute(index, {
                                supports1m: checked === true,
                              })
                            }
                          />
                          1M
                        </label>
                        <Button
                          type="button"
                          variant="ghost"
                          size="icon"
                          onClick={() =>
                            setRoutes((current) =>
                              current.filter((_, i) => i !== index),
                            )
                          }
                        >
                          <Trash2 className="h-4 w-4" />
                        </Button>
                      </div>
                    ))}
                  </div>
                ) : null}

                <Button
                  type="button"
                  variant="outline"
                  onClick={() =>
                    setRoutes((current) => [
                      ...current,
                      {
                        route: "",
                        model: "",
                        displayName: "",
                        supports1m: false,
                      },
                    ])
                  }
                >
                  <Plus className="mr-2 h-4 w-4" />
                  {t("claudeDesktop.addModel", { defaultValue: "添加模型" })}
                </Button>
              </div>
            </CollapsibleContent>
          </Collapsible>
        )}

        <FormField
          control={form.control}
          name="settingsConfig"
          render={() => (
            <FormItem className="space-y-0">
              <FormControl>
                <input type="hidden" />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        {showButtons && (
          <div className="flex justify-end gap-2">
            <Button variant="outline" type="button" onClick={onCancel}>
              {t("common.cancel")}
            </Button>
            <Button type="submit" disabled={form.formState.isSubmitting}>
              {submitLabel}
            </Button>
          </div>
        )}
      </form>
    </Form>
  );
}
