import { ChevronLeft, FolderSearch } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { AnimatedEllipsis } from "@/components/shared/animated-ellipsis";
import { HermesCategoryPicker } from "@/components/shared/hermes-category-picker";
import { ScopeTargetField } from "@/components/shared/scope-target-field";
import { useFocusTrap } from "@/hooks/use-focus-trap";
import { useScope } from "@/hooks/use-scope";
import { canInstallAtScope } from "@/lib/agent-capabilities";
import { openDirectoryPicker } from "@/lib/dialog";
import { humanizeError } from "@/lib/errors";
import { api } from "@/lib/invoke";
import { isDesktop } from "@/lib/transport";
import type { ConfigScope, DiscoveredSkill } from "@/lib/types";
import { agentDisplayName, sortAgents } from "@/lib/types";
import { useAgentStore } from "@/stores/agent-store";
import { useExtensionStore } from "@/stores/extension-store";
import { toast } from "@/stores/toast-store";

export type InstallMode = "git" | "local";

interface InstallDialogProps {
  open: boolean;
  mode: InstallMode;
  onClose: () => void;
}

type Phase = "input" | "select-skills";

export function InstallDialog({ open, mode, onClose }: InstallDialogProps) {
  const { t } = useTranslation("extensions");
  const { t: tc } = useTranslation("common");
  // Reuse the marketplace "kind not supported at scope" tooltip key.
  const { t: tm } = useTranslation("marketplace");
  const [source, setSource] = useState("");
  const [selectedAgents, setSelectedAgents] = useState<Set<string>>(new Set());
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [phase, setPhase] = useState<Phase>("input");
  const [discoveredSkills, setDiscoveredSkills] = useState<DiscoveredSkill[]>(
    [],
  );
  const [selectedSkills, setSelectedSkills] = useState<Set<string>>(new Set());
  const [cloneId, setCloneId] = useState<string | null>(null);
  const [hermesCategories, setHermesCategories] = useState<string[]>([]);
  const [hermesCategory, setHermesCategory] = useState<string>("local");
  const fetch = useExtensionStore((s) => s.fetch);
  const { agents, fetch: fetchAgents, agentOrder } = useAgentStore();
  const { scope } = useScope();
  // In single-scope mode the active scope IS the install target. In All-scopes
  // mode the user must pick via ScopeTargetField — start as null.
  const [installTargetScope, setInstallTargetScope] =
    useState<ConfigScope | null>(
      scope.type === "all" ? null : (scope as ConfigScope),
    );
  const dialogRef = useRef<HTMLDivElement>(null);
  const scanBtnRef = useRef<HTMLButtonElement>(null);

  useEffect(() => {
    fetchAgents();
  }, [fetchAgents]);

  const detectedAgents = sortAgents(
    agents.filter((a) => a.detected && a.enabled),
    agentOrder,
  );

  // Agents that can actually be installed to at the chosen scope. Before a
  // target is picked (All-scopes mode) every detected agent is a candidate;
  // once a project scope is chosen, scope-incapable agents (e.g. Hermes) drop
  // out so bulk select-all never queues them.
  const capableAgents = detectedAgents.filter(
    (a) =>
      !installTargetScope ||
      canInstallAtScope(a.name, "skill", installTargetScope),
  );

  // If only one agent detected, auto-select it
  const singleAgentName =
    detectedAgents.length === 1 ? detectedAgents[0].name : null;
  useEffect(() => {
    if (singleAgentName) {
      setSelectedAgents(new Set([singleAgentName]));
    }
  }, [singleAgentName]);

  const hermesSelected = selectedAgents.has("hermes");

  // Fetch Hermes categories when Hermes is a selected target.
  // biome-ignore lint/correctness/useExhaustiveDependencies: fetch once when Hermes is selected; reading hermesCategory only to keep a still-valid pick, so it must not be a dependency (would refetch on every category change).
  useEffect(() => {
    if (!hermesSelected) return;
    api
      .listHermesCategories()
      .then((cats) => {
        setHermesCategories(cats);
        if (!cats.includes(hermesCategory)) {
          setHermesCategory(cats[0] ?? "local");
        }
      })
      .catch(() => {});
  }, [hermesSelected]);

  // Reset form when closing
  useEffect(() => {
    if (!open) {
      setSource("");
      setError(null);
      setPhase("input");
      setDiscoveredSkills([]);
      setSelectedSkills(new Set());
      setCloneId(null);
      setHermesCategory("local");
      setInstallTargetScope(
        scope.type === "all" ? null : (scope as ConfigScope),
      );
    }
  }, [open, scope]);

  // Drop agents that don't support the chosen scope (e.g. Hermes at project).
  useEffect(() => {
    if (!installTargetScope) return;
    setSelectedAgents((prev) => {
      const next = new Set(
        [...prev].filter((name) =>
          canInstallAtScope(name, "skill", installTargetScope),
        ),
      );
      return next.size === prev.size ? prev : next;
    });
  }, [installTargetScope]);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape" && open) onClose();
    };
    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [onClose, open]);

  // Focus trap
  useFocusTrap(dialogRef, open);

  const toggleAgent = (name: string) => {
    setSelectedAgents((prev) => {
      const next = new Set(prev);
      if (next.has(name)) next.delete(name);
      else next.add(name);
      return next;
    });
  };

  // "All" reflects every agent installable at the current scope, so it shows
  // checked even when a scope-incapable agent (e.g. Hermes at project) is
  // present and hidden from selection.
  const allAgentsSelected =
    capableAgents.length > 0 &&
    capableAgents.every((a) => selectedAgents.has(a.name));
  const toggleAllAgents = () => {
    if (allAgentsSelected) {
      setSelectedAgents(new Set());
    } else {
      setSelectedAgents(new Set(capableAgents.map((a) => a.name)));
    }
  };

  const toggleSkill = (skillId: string) => {
    setSelectedSkills((prev) => {
      const next = new Set(prev);
      if (next.has(skillId)) next.delete(skillId);
      else next.add(skillId);
      return next;
    });
  };

  const allSkillsSelected =
    discoveredSkills.length > 0 &&
    discoveredSkills.every((s) => selectedSkills.has(s.skill_id));
  const toggleAllSkills = () => {
    if (allSkillsSelected) {
      setSelectedSkills(new Set());
    } else {
      setSelectedSkills(new Set(discoveredSkills.map((s) => s.skill_id)));
    }
  };

  const handleBrowse = async () => {
    const selected = await openDirectoryPicker({
      title: t("install.selectSkillDir"),
    });
    if (selected) setSource(selected);
  };

  const handleInstallAction = async () => {
    if (!source.trim() || selectedAgents.size === 0 || !installTargetScope) {
      return;
    }
    setLoading(true);
    setError(null);
    try {
      const effectiveHermesCategory = hermesCategory.trim() || "local";
      if (mode === "local") {
        const result = await api.installFromLocal(
          source.trim(),
          [...selectedAgents],
          installTargetScope,
          hermesSelected ? effectiveHermesCategory : undefined,
        );
        await fetch();
        onClose();
        toast.success(t("install.nameInstalled", { name: result.name }));
      } else {
        const result = await api.scanGitRepo(
          source.trim(),
          [...selectedAgents],
          installTargetScope,
        );
        if (result.type === "Installed") {
          await fetch();
          onClose();
          toast.success(
            t("install.nameInstalled", { name: result.result.name }),
          );
        } else if (result.type === "MultipleSkills") {
          setDiscoveredSkills(result.skills);
          setSelectedSkills(new Set(result.skills.map((s) => s.skill_id)));
          setCloneId(result.clone_id);
          setPhase("select-skills");
        } else {
          setError(t("install.noSkillsFound"));
        }
      }
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setLoading(false);
    }
  };

  const handleInstallSelected = async () => {
    if (!cloneId || selectedSkills.size === 0 || !installTargetScope) return;
    setLoading(true);
    setError(null);
    try {
      const results = await api.installScannedSkills(
        cloneId,
        [...selectedSkills],
        [...selectedAgents],
        installTargetScope,
      );
      await fetch();
      onClose();
      const uniqueNames = [...new Set(results.map((r) => r.name))];
      toast.success(
        uniqueNames.length === 1
          ? t("install.nameInstalled", { name: uniqueNames[0] })
          : t("page.installedCount", { count: uniqueNames.length }),
      );
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setLoading(false);
    }
  };

  const isGit = mode === "git";
  const title = isGit ? t("install.titleGit") : t("install.titleLocal");
  const description = isGit
    ? t("install.descGit")
    : isDesktop()
      ? t("install.descLocalDesktop")
      : t("install.descLocalWeb");
  const placeholder = isGit
    ? t("install.gitPlaceholder")
    : t("install.localPlaceholder");
  const buttonLabel = isGit ? (
    loading ? (
      <>
        {t("install.scanning")}
        <AnimatedEllipsis />
      </>
    ) : (
      t("install.install")
    )
  ) : loading ? (
    <>
      {t("install.installing")}
      <AnimatedEllipsis />
    </>
  ) : (
    t("install.install")
  );

  return (
    <div
      className="grid transition-[grid-template-rows] duration-[250ms]"
      style={{ gridTemplateRows: open ? "1fr" : "0fr" }}
    >
      <div className="overflow-hidden">
        <div
          ref={dialogRef}
          role="dialog"
          aria-modal="true"
          className="rounded-xl border border-border bg-card p-4 shadow-sm"
        >
          {phase === "input" ? (
            <>
              <h3 className="text-sm font-semibold">{title}</h3>
              <p className="mt-1 text-xs text-muted-foreground">
                {description}
              </p>
              <div className="mt-3 flex items-center gap-1.5">
                <input
                  type="text"
                  value={source}
                  onChange={(e) => setSource(e.target.value)}
                  onKeyDown={(e) =>
                    e.key === "Enter" &&
                    !e.nativeEvent.isComposing &&
                    e.keyCode !== 229 &&
                    !loading &&
                    scanBtnRef.current?.click()
                  }
                  placeholder={placeholder}
                  aria-label={
                    isGit ? t("install.gitUrlAria") : t("install.localPathAria")
                  }
                  aria-required="true"
                  aria-describedby={error ? "install-error" : undefined}
                  className="flex-1 rounded-lg border border-border bg-muted px-3 py-2 text-sm outline-none focus:border-ring focus:ring-2 focus:ring-ring/50"
                  disabled={loading}
                />
                {!isGit && isDesktop() && (
                  <button
                    onClick={handleBrowse}
                    disabled={loading}
                    className="shrink-0 rounded-lg border border-border bg-muted p-2 text-muted-foreground hover:text-foreground hover:bg-accent transition-colors disabled:opacity-40"
                    title={t("install.browseFolder")}
                  >
                    <FolderSearch size={16} />
                  </button>
                )}
              </div>
              {detectedAgents.length > 1 && (
                <div className="mt-3">
                  <span className="text-xs text-muted-foreground">
                    {t("install.installTo")}
                  </span>
                  <div className="mt-1.5 flex flex-wrap items-center gap-x-4 gap-y-1.5">
                    <label className="flex items-center gap-1.5 text-xs font-medium text-foreground">
                      <input
                        type="checkbox"
                        checked={allAgentsSelected}
                        onChange={toggleAllAgents}
                        disabled={loading}
                        className="rounded border-border accent-primary"
                      />
                      {t("install.allAgents")}
                    </label>
                    <span className="text-border">|</span>
                    {detectedAgents.map((a) => {
                      // ConfigScope ⊂ ScopeValue, so installTargetScope passes
                      // through canInstallAtScope's ScopeValue param directly.
                      const capableAtScope =
                        !installTargetScope ||
                        canInstallAtScope(a.name, "skill", installTargetScope);
                      return (
                        <label
                          key={a.name}
                          title={
                            capableAtScope
                              ? undefined
                              : tm("detail.kindNotSupported", {
                                  agent: a.name,
                                })
                          }
                          className={`flex items-center gap-1.5 text-xs ${
                            capableAtScope
                              ? "text-foreground"
                              : "text-muted-foreground/50"
                          }`}
                        >
                          <input
                            type="checkbox"
                            checked={
                              capableAtScope && selectedAgents.has(a.name)
                            }
                            onChange={() => toggleAgent(a.name)}
                            disabled={loading || !capableAtScope}
                            className="rounded border-border accent-primary"
                          />
                          {agentDisplayName(a.name)}
                        </label>
                      );
                    })}
                  </div>
                </div>
              )}
              <div className="mt-3">
                <ScopeTargetField
                  value={installTargetScope}
                  onChange={setInstallTargetScope}
                />
              </div>

              {/* Hermes category picker — only when Hermes is a selected target */}
              {hermesSelected && (
                <div className="mt-3">
                  <span className="text-xs text-muted-foreground">
                    Hermes category
                  </span>
                  <div className="mt-1.5">
                    <HermesCategoryPicker
                      categories={hermesCategories}
                      value={hermesCategory}
                      onChange={setHermesCategory}
                      disabled={loading}
                    />
                  </div>
                </div>
              )}
            </>
          ) : (
            <>
              <div className="flex items-center gap-2">
                <button
                  onClick={() => {
                    setPhase("input");
                    setError(null);
                  }}
                  disabled={loading}
                  className="shrink-0 rounded-lg p-1 text-muted-foreground hover:text-foreground"
                  aria-label={t("install.back")}
                >
                  <ChevronLeft size={16} />
                </button>
                <div>
                  <h3 className="text-sm font-semibold">
                    {t("install.selectSkillsTitle")}
                  </h3>
                  <p className="text-xs text-muted-foreground">
                    {t("install.skillsFound", {
                      count: discoveredSkills.length,
                    })}
                  </p>
                </div>
              </div>
              <div className="mt-3">
                <label className="flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium text-foreground hover:bg-muted/30 transition-colors">
                  <input
                    type="checkbox"
                    checked={allSkillsSelected}
                    onChange={toggleAllSkills}
                    disabled={loading}
                    className="rounded border-border accent-primary"
                  />
                  {t("install.allSkills")}
                </label>
                <div className="border-t border-border/50 mb-2" />
                <div className="flex flex-wrap gap-1.5 px-1">
                  {discoveredSkills.map((skill) => (
                    <label
                      key={skill.skill_id}
                      className="flex items-center gap-1.5 rounded-lg border border-border px-2.5 py-1 text-xs cursor-pointer hover:bg-muted/30 transition-colors"
                    >
                      <input
                        type="checkbox"
                        checked={selectedSkills.has(skill.skill_id)}
                        onChange={() => toggleSkill(skill.skill_id)}
                        disabled={loading}
                        className="rounded border-border accent-primary"
                      />
                      <span className="font-medium text-foreground">
                        {skill.name}
                      </span>
                    </label>
                  ))}
                </div>
              </div>
            </>
          )}

          {error && (
            <div
              id="install-error"
              className="mt-2 rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive"
            >
              {humanizeError(error)}
            </div>
          )}
          <div className="mt-3 flex items-center gap-2">
            {phase === "input" ? (
              <button
                ref={scanBtnRef}
                onClick={handleInstallAction}
                disabled={
                  loading ||
                  !source.trim() ||
                  selectedAgents.size === 0 ||
                  !installTargetScope
                }
                className="rounded-lg bg-primary px-4 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
              >
                {buttonLabel}
              </button>
            ) : (
              <button
                onClick={handleInstallSelected}
                disabled={loading || selectedSkills.size === 0}
                className="rounded-lg bg-primary px-4 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
              >
                {loading ? (
                  <>
                    {t("install.installing")}
                    <AnimatedEllipsis />
                  </>
                ) : selectedSkills.size > 0 ? (
                  t("install.installCount", { count: selectedSkills.size })
                ) : (
                  t("install.install")
                )}
              </button>
            )}
            <button
              onClick={onClose}
              disabled={loading}
              className="rounded-lg px-4 py-2 text-sm text-muted-foreground hover:text-foreground"
            >
              {tc("actions.cancel")}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}
