"use client";

import { LoaderCircleIcon, SearchIcon, X } from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect, useId, useRef, useState } from "react";

import { Badge } from "@/components/shadcn/badge/badge";
import { Input } from "@/components/shadcn/input/input";
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/shadcn/tooltip";
import { useUrlFilters } from "@/hooks/use-url-filters";
import { cn } from "@/lib/utils";

const SEARCH_DEBOUNCE_MS = 500;

interface DataTableSearchProps {
  /** Prefix for URL params to avoid conflicts (e.g., "findings" -> "findingsSearch") */
  paramPrefix?: string;

  /*
   * Controlled mode: Use these props to manage search via React state
   * instead of URL params. Useful for tables in drawers/modals to avoid
   * triggering page re-renders when searching.
   */
  controlledValue?: string;
  onSearchChange?: (value: string) => void;
  /**
   * Called when the user commits a search (pressing Enter).
   * When provided, the search is only "committed" on Enter, while
   * onSearchChange still fires on every keystroke for responsive display.
   * Use this to avoid remounting child components on every keystroke.
   */
  onSearchCommit?: (value: string) => void;
  placeholder?: string;
  /** Badge shown inside the search input (e.g., active drill-down group title) */
  badge?: { label: string; onDismiss: () => void };
}

export const DataTableSearch = ({
  paramPrefix = "",
  controlledValue,
  onSearchChange,
  onSearchCommit,
  placeholder = "Search... (press Enter)",
  badge,
}: DataTableSearchProps) => {
  const searchParams = useSearchParams();
  const pathname = usePathname();
  const router = useRouter();
  const { updateFilter } = useUrlFilters();
  const [internalValue, setInternalValue] = useState("");
  // In controlled mode, track display value separately for immediate feedback
  const [displayValue, setDisplayValue] = useState(controlledValue ?? "");
  const [isLoading, setIsLoading] = useState(false);
  const [isFocused, setIsFocused] = useState(false);
  const id = useId();
  const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const inputRef = useRef<HTMLInputElement>(null);

  // Use controlled value if provided, otherwise internal state
  const isControlled = controlledValue !== undefined && onSearchChange;
  // For display: use displayValue in controlled mode (for responsive typing), internalValue otherwise
  const value = isControlled ? displayValue : internalValue;

  // Force expanded when badge is present
  const hasBadge = !!badge;

  // Sync displayValue when controlledValue changes externally (e.g., clear filters)
  useEffect(() => {
    if (isControlled) {
      setDisplayValue(controlledValue);
    }
  }, [controlledValue, isControlled]);

  // Determine param names based on prefix
  const searchParam = paramPrefix ? `${paramPrefix}Search` : "filter[search]";
  const pageParam = paramPrefix ? `${paramPrefix}Page` : "page";

  // Sync with URL on mount (only for uncontrolled mode)
  useEffect(() => {
    if (isControlled) return;
    const searchFromUrl = searchParams.get(searchParam) || "";
    setInternalValue(searchFromUrl);
  }, [searchParams, searchParam, isControlled]);

  // Handle input change with debounce
  const handleChange = (newValue: string) => {
    if (isControlled) {
      setDisplayValue(newValue);

      if (debounceTimeoutRef.current) {
        clearTimeout(debounceTimeoutRef.current);
      }

      if (onSearchCommit) {
        // Enter-to-commit mode: sync parent immediately (no debounce, no loading).
        // The actual search commit happens on Enter via onSearchCommit.
        onSearchChange(newValue);
        return;
      }

      // Standard controlled mode: debounce the callback
      setIsLoading(true);
      debounceTimeoutRef.current = setTimeout(() => {
        onSearchChange(newValue);
        setIsLoading(false);
      }, SEARCH_DEBOUNCE_MS);
      return;
    }

    // Uncontrolled mode: only update display value on keystroke.
    // The actual URL update happens on Enter (see onKeyDown handler).
    setInternalValue(newValue);
  };

  // Cleanup timeout on unmount
  useEffect(() => {
    return () => {
      if (debounceTimeoutRef.current) {
        clearTimeout(debounceTimeoutRef.current);
      }
    };
  }, []);

  const handleFocus = () => {
    setIsFocused(true);
  };

  const handleBlur = () => {
    setIsFocused(false);
  };

  return (
    <div
      className={cn(
        "relative flex items-center",
        hasBadge ? "w-[28rem]" : "w-64",
      )}
    >
      <div className="relative w-full">
        <div
          className={cn(
            "border-border-neutral-tertiary bg-bg-neutral-tertiary hover:bg-bg-neutral-secondary flex items-center gap-1.5 rounded-md border transition-colors",
            isFocused && "border-border-input-primary-pressed",
          )}
        >
          <div className="flex shrink-0 items-center pl-3">
            <SearchIcon className="text-text-neutral-tertiary size-4" />
          </div>

          {hasBadge && (
            <Tooltip>
              <TooltipTrigger asChild>
                <Badge
                  variant="tag"
                  className="max-w-[200px] shrink-0 cursor-default gap-1 truncate"
                >
                  <span className="truncate">{badge.label}</span>
                  <button
                    type="button"
                    aria-label="Dismiss filter"
                    className="hover:text-text-neutral-primary ml-0.5 shrink-0"
                    onClick={(e) => {
                      e.stopPropagation();
                      badge.onDismiss();
                    }}
                  >
                    <X className="size-3" />
                  </button>
                </Badge>
              </TooltipTrigger>
              <TooltipContent>{badge.label}</TooltipContent>
            </Tooltip>
          )}

          <Input
            ref={inputRef}
            id={id}
            type="search"
            placeholder={placeholder}
            value={value}
            onChange={(e) => handleChange(e.target.value)}
            onKeyDown={(e) => {
              if (e.key !== "Enter") return;

              // Cancel any pending debounce — Enter commits immediately
              if (debounceTimeoutRef.current) {
                clearTimeout(debounceTimeoutRef.current);
                debounceTimeoutRef.current = null;
              }
              setIsLoading(false);

              // Controlled mode with explicit commit callback
              if (isControlled && onSearchCommit) {
                onSearchCommit(value);
                return;
              }

              // Uncontrolled mode: immediate URL update (shortcut for debounce)
              if (!isControlled) {
                if (paramPrefix) {
                  const params = new URLSearchParams(searchParams.toString());
                  if (value) {
                    params.set(searchParam, value);
                  } else {
                    params.delete(searchParam);
                  }
                  params.set(pageParam, "1");
                  router.push(`${pathname}?${params.toString()}`, {
                    scroll: false,
                  });
                } else {
                  updateFilter("search", value || null);
                }
              }
            }}
            onFocus={handleFocus}
            onBlur={handleBlur}
            className="h-9 min-w-0 flex-1 border-0 bg-transparent pr-9 shadow-none hover:bg-transparent focus:border-0 focus:ring-0 focus:ring-offset-0 focus-visible:ring-0 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none"
          />

          {isLoading && (
            <div className="flex shrink-0 items-center pr-3">
              <LoaderCircleIcon className="text-text-neutral-tertiary size-4 animate-spin" />
            </div>
          )}
        </div>
      </div>
    </div>
  );
};
