import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { client } from "@/client";
import { formatMention } from "@/lib/ir/parser";
import { GitBranch, Loader2 } from "lucide-react";
import { cn } from "@/lib/utils";
import type { RepoResponse } from "@/client/models/repoResponse";

interface MentionState {
  /** Position in text where the @ that started this mention is */
  startIndex: number;
  /** The query text after @ (what user is typing to search) */
  query: string;
}

interface Props {
  workspaceName: string;
  textValue: string;
  onInsertMention: (
    mentionText: string,
    replaceFrom: number,
    replaceTo: number,
  ) => void;
}

/**
 * Inline repo mention popover triggered by typing @ in the chat input.
 * Searches repos in the current workspace and inserts IR mention syntax.
 */
export function RepoMentionPopover({
  workspaceName,
  textValue,
  onInsertMention,
}: Props) {
  const [repos, setRepos] = useState<RepoResponse[]>([]);
  const [loading, setLoading] = useState(false);
  const [selectedIndex, setSelectedIndex] = useState(0);
  const searchAbortRef = useRef<AbortController | null>(null);

  // Detect @ mention trigger from text value.
  const mentionState: MentionState | null = useMemo(() => {
    if (!textValue) return null;

    // Find the last @ that could start a mention.
    // Exclude @ that are part of existing @[type:id:label] syntax.
    const atIndex = textValue.lastIndexOf("@");
    if (atIndex === -1) return null;

    const afterAt = textValue.slice(atIndex);

    // If it looks like a completed @[type:id:label], don't trigger.
    if (/^@\[[a-z_]+:[^:\]]+:[^\]]+\]/.test(afterAt)) return null;

    // If there's already a complete @[...] before this one, check if
    // this @ is inside or right after it.
    const mentionRegex = /@\[[a-z_]+:[^:\]]+:[^\]]+\]/g;
    let match;
    while ((match = mentionRegex.exec(textValue)) !== null) {
      const end = match.index + match[0].length;
      if (atIndex < end && atIndex >= match.index) {
        // This @ is inside an existing mention, skip.
        return null;
      }
    }

    const query = textValue.slice(atIndex + 1);

    // Don't trigger if query contains space (mention is done).
    if (query.includes(" ")) return null;

    // Don't trigger on empty query after @ (wait for user to type).
    // But we show the popover to give immediate feedback.
    return { startIndex: atIndex, query };
  }, [textValue]);

  // Fetch repos when query changes.
  useEffect(() => {
    if (!mentionState || !workspaceName) {
      setRepos([]);
      return;
    }

    // Cancel previous search.
    searchAbortRef.current?.abort();
    const controller = new AbortController();
    searchAbortRef.current = controller;

    setLoading(true);

    client
      .gitListRepos(workspaceName, {
        search: mentionState.query || undefined,
        limit: 8,
      })
      .then(({ data }) => {
        if (!controller.signal.aborted) {
          setRepos(data);
          setSelectedIndex(0);
        }
      })
      .catch((err: unknown) => {
        if (!controller.signal.aborted) {
          console.error("Failed to search repos for mention:", err);
          setRepos([]);
        }
      })
      .finally(() => {
        if (!controller.signal.aborted) setLoading(false);
      });

    return () => controller.abort();
  }, [mentionState?.query, workspaceName]);

  const open = mentionState !== null;

  const handleSelect = useCallback(
    (repo: RepoResponse) => {
      if (!mentionState) return;
      const mentionText = formatMention("repo", repo.name, repo.name) + " ";
      const replaceTo = mentionState.startIndex + mentionState.query.length + 1;
      onInsertMention(mentionText, mentionState.startIndex, replaceTo);
    },
    [mentionState, onInsertMention],
  );

  // Keyboard navigation when popover is open.
  // Use capture phase so we intercept before the textarea's own Enter handler
  // (which would otherwise submit the form).
  useEffect(() => {
    if (!open || repos.length === 0) return;

    const handleKeyDown = (e: KeyboardEvent) => {
      switch (e.key) {
        case "ArrowDown":
          e.preventDefault();
          e.stopPropagation();
          setSelectedIndex((i) => Math.min(i + 1, repos.length - 1));
          break;
        case "ArrowUp":
          e.preventDefault();
          e.stopPropagation();
          setSelectedIndex((i) => Math.max(i - 1, 0));
          break;
        case "Enter":
          if (repos[selectedIndex]) {
            e.preventDefault();
            e.stopPropagation();
            handleSelect(repos[selectedIndex]);
          }
          break;
        case "Escape":
          // Let the user naturally dismiss by continuing to type.
          break;
      }
    };

    // Capture phase: fire before the textarea's own keydown handler.
    document.addEventListener("keydown", handleKeyDown, true);
    return () => document.removeEventListener("keydown", handleKeyDown, true);
  }, [open, repos, selectedIndex, handleSelect]);

  if (!open) return null;

  return (
    <div className="absolute bottom-full left-0 mb-2 w-80">
      <div className="overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg ring-1 ring-foreground/5">
        {/* Header */}
        <div className="border-b border-border/50 px-3 py-2">
          <span className="text-[11px] text-muted-foreground">
            Repositories in{" "}
            <span className="font-medium text-foreground/80">
              {workspaceName}
            </span>
          </span>
        </div>

        {/* Results */}
        <div className="max-h-56 overflow-y-auto p-1">
          {loading && (
            <div className="flex items-center justify-center py-8">
              <Loader2 className="size-4 animate-spin text-muted-foreground/60" />
            </div>
          )}

          {!loading && repos.length === 0 && (
            <div className="py-6 text-center text-xs text-muted-foreground/60">
              {mentionState.query
                ? `No repos matching "${mentionState.query}"`
                : "No repositories found"}
            </div>
          )}

          {!loading &&
            repos.map((repo, i) => (
              <button
                key={repo.id}
                type="button"
                className={cn(
                  "flex w-full items-center gap-3 rounded-md px-3 py-2 text-left transition-colors",
                  i === selectedIndex
                    ? "bg-accent text-accent-foreground"
                    : "hover:bg-muted/50",
                )}
                onClick={() => handleSelect(repo)}
                onMouseEnter={() => setSelectedIndex(i)}
              >
                <GitBranch className="size-4 shrink-0 text-emerald-500" />
                <div className="min-w-0 flex-1">
                  <p className="truncate text-sm font-medium">{repo.name}</p>
                  {repo.description && (
                    <p className="truncate text-[11px] text-muted-foreground">
                      {repo.description}
                    </p>
                  )}
                </div>
                {repo.is_archived && (
                  <span className="shrink-0 rounded bg-warning/10 px-1.5 py-0.5 text-[10px] font-medium text-warning dark:bg-warning/20 dark:text-warning">
                    archived
                  </span>
                )}
              </button>
            ))}
        </div>
      </div>
    </div>
  );
}
