"use client";

import { invoke } from "@tauri-apps/api/core";
import { emit } from "@tauri-apps/api/event";
import { useCallback, useEffect, useReducer, useState } from "react";
import { useTranslation } from "react-i18next";
import { LuUpload } from "react-icons/lu";
import { toast } from "sonner";
import { LoadingButton } from "@/components/loading-button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { ScrollArea } from "@/components/ui/scroll-area";
import { StepTransition } from "@/components/ui/step-transition";
import { getCurrentOS } from "@/lib/browser-utils";
import { resolveAmbiguousProxyLine } from "@/lib/proxy-string";
import type {
  ParsedProxyLine,
  ProxyImportResult,
  ProxyParseResult,
} from "@/types";
import { RippleButton } from "./ui/ripple";

interface ProxyImportDialogProps {
  isOpen: boolean;
  onClose: () => void;
}

type ImportStep = "dropzone" | "preview" | "ambiguous" | "result";

const STEP_ORDER: Record<ImportStep, number> = {
  dropzone: 0,
  ambiguous: 1,
  preview: 2,
  result: 3,
};

interface StepState {
  step: ImportStep;
  direction: 1 | -1;
}

function transitionStep(current: StepState, next: ImportStep): StepState {
  return {
    step: next,
    direction: STEP_ORDER[next] >= STEP_ORDER[current.step] ? 1 : -1,
  };
}

interface AmbiguousProxy {
  line: string;
  possible_formats: string[];
  selectedFormat?: string;
}

export function ProxyImportDialog({ isOpen, onClose }: ProxyImportDialogProps) {
  const { t } = useTranslation();
  const [{ step, direction: stepDirection }, setStep] = useReducer(
    transitionStep,
    { step: "dropzone", direction: 1 },
  );
  const [isDragOver, setIsDragOver] = useState(false);
  const [parsedProxies, setParsedProxies] = useState<ParsedProxyLine[]>([]);
  const [ambiguousProxies, setAmbiguousProxies] = useState<AmbiguousProxy[]>(
    [],
  );
  const [invalidProxies, setInvalidProxies] = useState<
    { line: string; reason: string }[]
  >([]);
  const [importResult, setImportResult] = useState<ProxyImportResult | null>(
    null,
  );
  const [isImporting, setIsImporting] = useState(false);
  const [namePrefix, setNamePrefix] = useState(
    t("proxies.importDialog.namePrefixDefault"),
  );
  const os = getCurrentOS();
  const modKey = os === "macos" ? "⌘" : "Ctrl";

  const resetState = useCallback(() => {
    setStep("dropzone");
    setIsDragOver(false);
    setParsedProxies([]);
    setAmbiguousProxies([]);
    setInvalidProxies([]);
    setImportResult(null);
    setIsImporting(false);
    setNamePrefix(t("proxies.importDialog.namePrefixDefault"));
  }, [t]);

  const processContent = useCallback(
    async (content: string, isJson: boolean, _filename = "") => {
      try {
        if (isJson) {
          setIsImporting(true);
          const result = await invoke<ProxyImportResult>(
            "import_proxies_json",
            {
              content,
            },
          );
          setImportResult(result);
          setStep("result");
          await emit("stored-proxies-changed");
        } else {
          const results = await invoke<ProxyParseResult[]>(
            "parse_txt_proxies",
            {
              content,
            },
          );

          const parsed: ParsedProxyLine[] = [];
          const ambiguous: AmbiguousProxy[] = [];
          const invalid: { line: string; reason: string }[] = [];

          for (const result of results) {
            if (result.status === "parsed") {
              parsed.push(result);
            } else if (result.status === "ambiguous") {
              ambiguous.push({
                line: result.line,
                possible_formats: result.possible_formats,
              });
            } else if (result.status === "invalid") {
              invalid.push({ line: result.line, reason: result.reason });
            }
          }

          setParsedProxies(parsed);
          setAmbiguousProxies(ambiguous);
          setInvalidProxies(invalid);

          if (ambiguous.length > 0) {
            setStep("ambiguous");
          } else if (parsed.length > 0) {
            setStep("preview");
          } else {
            toast.error(t("proxies.importDialog.noValidProxies"));
          }
        }
      } catch (error) {
        console.error("Failed to process content:", error);
        toast.error(
          error instanceof Error
            ? error.message
            : t("proxies.importDialog.fileProcessError"),
        );
      } finally {
        setIsImporting(false);
      }
    },
    [t],
  );

  const handleFileRead = useCallback(
    (file: File) => {
      const reader = new FileReader();
      reader.onload = (e) => {
        const content = e.target?.result as string;
        const isJson = file.name.endsWith(".json");
        void processContent(content, isJson, file.name);
      };
      reader.onerror = () => {
        toast.error(t("proxies.importDialog.fileReadError"));
      };
      reader.readAsText(file);
    },
    [processContent, t],
  );

  const handleDrop = useCallback(
    (e: React.DragEvent<HTMLDivElement>) => {
      e.preventDefault();
      setIsDragOver(false);

      const files = Array.from(e.dataTransfer.files);
      const validFile = files.find(
        (f) => f.name.endsWith(".json") || f.name.endsWith(".txt"),
      );

      if (validFile) {
        handleFileRead(validFile);
      } else {
        toast.error(t("proxies.importDialog.wrongFileType"));
      }
    },
    [handleFileRead, t],
  );

  const handleDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    setIsDragOver(true);
  }, []);

  const handleDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    setIsDragOver(false);
  }, []);

  // Handle paste from clipboard
  useEffect(() => {
    if (!isOpen || step !== "dropzone") return;

    const handlePaste = (e: ClipboardEvent) => {
      const text = e.clipboardData?.getData("text");
      if (text) {
        // Try to detect if it's JSON
        const trimmed = text.trim();
        const isJson =
          (trimmed.startsWith("{") && trimmed.endsWith("}")) ||
          (trimmed.startsWith("[") && trimmed.endsWith("]"));
        // Use "pasted.txt" as filename to trigger content-based detection
        void processContent(text, isJson, "pasted.txt");
      }
    };

    document.addEventListener("paste", handlePaste);
    return () => {
      document.removeEventListener("paste", handlePaste);
    };
  }, [isOpen, step, processContent]);

  const handleImport = useCallback(async () => {
    setIsImporting(true);
    try {
      const result = await invoke<ProxyImportResult>(
        "import_proxies_from_parsed",
        {
          parsedProxies,
          namePrefix:
            namePrefix.trim() || t("proxies.importDialog.namePrefixDefault"),
        },
      );
      setImportResult(result);
      setStep("result");
      await emit("stored-proxies-changed");
    } catch (error) {
      console.error("Failed to import proxies:", error);
      toast.error(
        error instanceof Error
          ? error.message
          : t("proxies.importDialog.failed"),
      );
    } finally {
      setIsImporting(false);
    }
  }, [parsedProxies, namePrefix, t]);

  const handleAmbiguousFormatSelect = useCallback(
    (index: number, format: string) => {
      setAmbiguousProxies((prev) =>
        prev.map((p, i) =>
          i === index ? { ...p, selectedFormat: format } : p,
        ),
      );
    },
    [],
  );

  const handleResolveAmbiguous = useCallback(() => {
    const resolved = ambiguousProxies.flatMap((p) => {
      const parsed = p.selectedFormat
        ? resolveAmbiguousProxyLine(p.line, p.selectedFormat)
        : null;
      return parsed ? [parsed] : [];
    });

    setParsedProxies((prev) => [...prev, ...resolved]);
    setStep("preview");
  }, [ambiguousProxies]);

  const handleClose = useCallback(() => {
    resetState();
    onClose();
  }, [resetState, onClose]);

  return (
    <Dialog open={isOpen} onOpenChange={handleClose}>
      <DialogContent className="max-w-2xl">
        <DialogHeader>
          <DialogTitle>{t("proxies.importDialog.title")}</DialogTitle>
          <StepTransition
            transitionKey={`description-${step}`}
            direction={stepDirection}
          >
            <DialogDescription>
              {step === "dropzone" && t("proxies.importDialog.descDropzone")}
              {step === "preview" && t("proxies.importDialog.descPreview")}
              {step === "ambiguous" && t("proxies.importDialog.descAmbiguous")}
              {step === "result" && t("proxies.importDialog.descResult")}
            </DialogDescription>
          </StepTransition>
        </DialogHeader>

        <StepTransition
          transitionKey={step}
          direction={stepDirection}
          className="grid gap-4"
        >
          {step === "dropzone" && (
            <div className="space-y-4">
              <div
                role="button"
                tabIndex={0}
                className={`
                flex flex-col items-center justify-center
                border-2 border-dashed rounded-lg p-8
                cursor-pointer transition-[color,background-color,border-color,transform]
                duration-[160ms] ease-[var(--ease-out)] motion-reduce:transform-none
                ${isDragOver ? "scale-[1.005] border-primary bg-primary/5" : "border-muted-foreground/25 hover:border-muted-foreground/50"}
              `}
                onDrop={handleDrop}
                onDragOver={handleDragOver}
                onDragLeave={handleDragLeave}
                onClick={() =>
                  document.getElementById("proxy-file-input")?.click()
                }
                onKeyDown={(e) => {
                  if (e.key === "Enter" || e.key === " ") {
                    e.preventDefault();
                    document.getElementById("proxy-file-input")?.click();
                  }
                }}
              >
                <LuUpload
                  className={`mb-4 size-10 text-muted-foreground transition-transform duration-[160ms] ease-[var(--ease-out)] motion-reduce:transform-none ${
                    isDragOver ? "-translate-y-0.5 scale-105" : ""
                  }`}
                />
                <p className="text-center text-sm text-muted-foreground">
                  {t("proxies.importDialog.dropzonePrompt")}
                  <br />
                  <span className="text-xs">
                    {t("proxies.importDialog.dropzoneFormats")}
                  </span>
                </p>
                <input
                  id="proxy-file-input"
                  type="file"
                  accept=".json,.txt"
                  className="hidden"
                  onChange={(e) => {
                    const file = e.target.files?.[0];
                    if (file) handleFileRead(file);
                    e.target.value = "";
                  }}
                />
              </div>
              <p className="text-center text-xs text-muted-foreground">
                {t("proxies.importDialog.pasteHint", { modKey })}
              </p>
            </div>
          )}

          {step === "preview" && (
            <div className="space-y-4">
              <div className="space-y-2">
                <Label htmlFor="name-prefix">
                  {t("proxies.importDialog.namePrefix")}
                </Label>
                <Input
                  id="name-prefix"
                  placeholder={t("proxies.importDialog.namePrefixDefault")}
                  value={namePrefix}
                  onChange={(e) => {
                    setNamePrefix(e.target.value);
                  }}
                />
                <p className="text-xs text-muted-foreground">
                  {t("proxies.importDialog.namePrefixHint", {
                    prefix:
                      namePrefix || t("proxies.importDialog.namePrefixDefault"),
                  })}
                </p>
              </div>

              <div className="space-y-2">
                <Label>
                  {t("proxies.importDialog.proxiesToImport", {
                    count: parsedProxies.length,
                  })}
                  {invalidProxies.length > 0 && (
                    <span className="ml-2 text-muted-foreground">
                      {t("proxies.importDialog.invalidCount", {
                        count: invalidProxies.length,
                      })}
                    </span>
                  )}
                </Label>
                <ScrollArea className="h-[clamp(120px,30vh,400px)] rounded-md border">
                  <div className="space-y-1 p-2">
                    {parsedProxies.map((proxy, i) => (
                      <div
                        key={`${proxy.original_line}-${i}`}
                        className="rounded bg-muted/30 p-2 font-mono text-xs break-all"
                      >
                        <span className="text-primary-text">
                          {proxy.proxy_type}://
                        </span>
                        {proxy.username && (
                          <span className="text-muted-foreground">
                            {proxy.username}:***@
                          </span>
                        )}
                        <span>
                          {proxy.host}:{proxy.port}
                        </span>
                      </div>
                    ))}
                  </div>
                </ScrollArea>
              </div>
            </div>
          )}

          {step === "ambiguous" && (
            <div className="space-y-4">
              <p className="text-sm text-muted-foreground">
                {t("proxies.importDialog.ambiguousIntro")}
              </p>
              <ScrollArea className="h-[clamp(150px,35vh,450px)] rounded-md border">
                <div className="space-y-4 p-3">
                  {ambiguousProxies.map((proxy, i) => (
                    <div
                      key={`${proxy.line}-${i}`}
                      className="space-y-2 border-b pb-3 last:border-0"
                    >
                      <code className="block rounded bg-muted px-2 py-1 text-xs break-all">
                        {proxy.line}
                      </code>
                      <div className="flex flex-col gap-2">
                        {proxy.possible_formats.map((format) => (
                          <label
                            key={format}
                            className="flex cursor-pointer items-center gap-2"
                          >
                            <input
                              type="radio"
                              name={`format-${i}`}
                              checked={proxy.selectedFormat === format}
                              onChange={() => {
                                handleAmbiguousFormatSelect(i, format);
                              }}
                              className="accent-primary"
                            />
                            <span className="text-xs">{format}</span>
                          </label>
                        ))}
                      </div>
                    </div>
                  ))}
                </div>
              </ScrollArea>
            </div>
          )}

          {step === "result" && importResult && (
            <div className="space-y-4">
              <div className="space-y-2 rounded-lg bg-muted/30 p-4">
                <div className="flex justify-between">
                  <span className="text-sm">
                    {t("proxies.importDialog.imported")}
                  </span>
                  <span className="text-sm font-medium text-success-text">
                    {importResult.imported_count}
                  </span>
                </div>
                {importResult.skipped_count > 0 && (
                  <div className="flex justify-between">
                    <span className="text-sm">
                      {t("proxies.importDialog.skippedDuplicates")}
                    </span>
                    <span className="text-sm font-medium text-warning-text">
                      {importResult.skipped_count}
                    </span>
                  </div>
                )}
                {importResult.errors.length > 0 && (
                  <div className="flex justify-between">
                    <span className="text-sm">
                      {t("proxies.importDialog.errors")}
                    </span>
                    <span className="text-sm font-medium text-destructive-text">
                      {importResult.errors.length}
                    </span>
                  </div>
                )}
              </div>

              {importResult.errors.length > 0 && (
                <div className="space-y-2">
                  <Label>{t("proxies.importDialog.errors")}</Label>
                  <ScrollArea className="h-[100px] rounded-md border">
                    <div className="space-y-1 p-2">
                      {importResult.errors.map((error, i) => (
                        <div
                          key={`error-${i}`}
                          className="text-xs text-destructive-text"
                        >
                          {error}
                        </div>
                      ))}
                    </div>
                  </ScrollArea>
                </div>
              )}
            </div>
          )}

          <DialogFooter>
            {step === "dropzone" && (
              <RippleButton variant="outline" onClick={handleClose}>
                {t("common.buttons.cancel")}
              </RippleButton>
            )}

            {step === "preview" && (
              <>
                <RippleButton variant="outline" onClick={resetState}>
                  {t("common.buttons.back")}
                </RippleButton>
                <LoadingButton
                  isLoading={isImporting}
                  onClick={() => void handleImport()}
                  disabled={parsedProxies.length === 0}
                >
                  {t("proxies.importDialog.importButton", {
                    count: parsedProxies.length,
                  })}
                </LoadingButton>
              </>
            )}

            {step === "ambiguous" && (
              <>
                <RippleButton variant="outline" onClick={resetState}>
                  {t("common.buttons.back")}
                </RippleButton>
                <RippleButton
                  onClick={handleResolveAmbiguous}
                  disabled={ambiguousProxies.some((p) => !p.selectedFormat)}
                >
                  {t("proxies.importDialog.continueButton")}
                </RippleButton>
              </>
            )}

            {step === "result" && (
              <RippleButton onClick={handleClose}>
                {t("proxies.importDialog.doneButton")}
              </RippleButton>
            )}
          </DialogFooter>
        </StepTransition>
      </DialogContent>
    </Dialog>
  );
}
