"use client";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Drawer,
  DrawerClose,
  DrawerContent,
  DrawerDescription,
  DrawerFooter,
  DrawerHeader,
  DrawerTitle,
  DrawerTrigger,
} from "@/components/ui/drawer";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; // Assuming shadcn Label
import { cn } from "@/lib/utils";
import { ListFilter, Loader2, X } from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect, useMemo, useState, useTransition } from "react";
import { RadioGroup, RadioGroupItem } from "./ui/radio-group";

interface CheckboxGroupProps {
  label: string;
  options: Array<{ value: string; label: string }>;
  selectedValues: string[];
  onChange: (values: string[]) => void;
  name: string;
}

const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
  label,
  options,
  selectedValues,
  onChange,
  name,
}) => {
  const handleCheckedChange = (value: string, checked: boolean) => {
    if (checked) {
      onChange([...selectedValues, value]);
    } else {
      onChange(selectedValues.filter((v) => v !== value));
    }
  };

  return (
    <div className="flex flex-col gap-2 p-2 border rounded-md">
      <Label className="font-semibold text-sm">{label}</Label>
      <div className="flex flex-col gap-2">
        {options.map((option) => (
          <div key={`${name}-${option.value}`} className="flex items-center space-x-2">
            <Checkbox
              id={`${name}-${option.value}`}
              checked={selectedValues.includes(option.value)}
              onCheckedChange={(checked) =>
                handleCheckedChange(option.value, !!checked)
              }
            />
            <Label htmlFor={`${name}-${option.value}`}>{option.label}</Label>
          </div>
        ))}
      </div>
    </div>
  );
};


type FilterOption = { value: string; label: string };

type FilterInputConfig<TKey extends string> =
  | {
    name: TKey;
    label: string;
    placeholder: string;
    type: "string" | "number";
  }
  | {
    name: TKey;
    label: string;
    type: "checkbox-group";
    options: FilterOption[];
    serialize?: (values: string[]) => string;
    deserialize?: (urlValue: string) => string[];
  }
  | {
    name: TKey;
    label: string;
    type: "radio-group";
    options: FilterOption[];
  };

// Utility type to extract the config for a specific key from the array
type GetConfigForKey<TKey, TConfigArray extends FilterInputConfig<string>[]> =
  TConfigArray extends ReadonlyArray<infer U>
  ? U extends FilterInputConfig<string> & { name: TKey }
  ? U
  : never
  : never;

// This type maps filter keys to their appropriate value types (string or string[])
type FilterValues<
  TFilterKeys extends string,
  TInputs extends FilterInputConfig<string>[],
> = {
    [K in TFilterKeys]: GetConfigForKey<K, TInputs> extends { type: "checkbox-group" }
    ? string[]
    : GetConfigForKey<K, TInputs> extends { type: "radio-group" }
    ? string
    : string;
  };

// Default serialization/deserialization for checkbox-group if not provided
const defaultSerialize = (values: string[]) => values.join(",");
const defaultDeserialize = (urlValue: string) =>
  urlValue ? urlValue.split(",") : [];

// --- Component Props ---
interface DynamicFilterProps<
  TFilterKeys extends string,
  TInputs extends FilterInputConfig<TFilterKeys>[],
> {
  inputs: TInputs;
  title?: string;
  description?: string;
}

// --- Main DynamicFilter Component ---
export default function DynamicFilter<
  TFilterKeys extends string,
  TInputs extends FilterInputConfig<TFilterKeys>[],
>({
  inputs,
  title = "Filters",
  description = "Select your desired filters here",
}: DynamicFilterProps<TFilterKeys, TInputs>) {
  const { replace } = useRouter();
  const [isOpen, setIsOpen] = useState(false);
  const [disabled, startTransition] = useTransition();
  const searchParams = useSearchParams();
  const pathname = usePathname();

  // Use a ref or memoized value for URLSearchParams to avoid re-creations
  // and ensure it's always based on the latest searchParams.
  const currentParams = useMemo(
    () => new URLSearchParams(searchParams.toString()),
    [searchParams],
  );

  // Initialize local state for input values based on URL
  const initialInputState = useMemo(() => {
    const initialState: Partial<FilterValues<TFilterKeys, TInputs>> = {};
    for (const input of inputs) {
      const urlValue = searchParams.get(input.name);
      if (input.type === "checkbox-group") {
        const deserialize = input.deserialize || defaultDeserialize;
        (initialState as FilterValues<TFilterKeys, TInputs>)[input.name] = deserialize(urlValue || "") as FilterValues<TFilterKeys, TInputs>[typeof input.name];
      } else {
        (initialState as FilterValues<TFilterKeys, TInputs>)[input.name] = (urlValue || "") as FilterValues<TFilterKeys, TInputs>[typeof input.name];
      }
    }
    return initialState as FilterValues<TFilterKeys, TInputs>;
  }, [inputs, searchParams]); // Re-initialize if inputs config or URL searchParams change

  const [inputValues, setInputValues] = useState<
    FilterValues<TFilterKeys, TInputs>
  >(initialInputState);

  // Update local state if URL searchParams change while drawer is closed
  // This is important if filters are applied externally or on page load
  useEffect(() => {
    if (!isOpen) {
      setInputValues(initialInputState);
    }
  }, [isOpen, initialInputState]);


  // Handler for text/number input changes
  const handleTextOrNumberInputChange =
    (name: TFilterKeys) => (e: React.ChangeEvent<HTMLInputElement>) => {
      setInputValues((prev) => ({
        ...prev,
        [name]: e.target.value,
      }));
    };

  // Handler for checkbox group changes
  const handleCheckboxGroupChange =
    (name: TFilterKeys) => (values: string[]) => {
      setInputValues((prev) => ({
        ...prev,
        [name]: values,
      }));
    };

  // Handles applying all filters
  const handleApplyFilters = () => {
    const newParams = new URLSearchParams(currentParams.toString()); // Start fresh with current params

    for (const input of inputs) {
      const value = inputValues[input.name];

      if (input.type === "checkbox-group") {
        const serialize = input.serialize || defaultSerialize;
        const serializedValue = serialize(value as string[]);
        if (serializedValue) {
          newParams.set(input.name, serializedValue);
        } else {
          newParams.delete(input.name);
        }
      } else {
        // String/Number inputs
        if (value) {
          newParams.set(input.name, value as string);
        } else {
          newParams.delete(input.name);
        }
      }
    }

    newParams.set("page", "1"); // Always reset page on filter apply

    startTransition(() => {
      replace(`${pathname}?${newParams.toString()}`);
      setIsOpen(false); // Close the drawer after applying filters
    });
  };

  // Handles clearing all filters
  const handleClearFilters = () => {

    // Reset local input values
    const clearedInputState: Partial<FilterValues<TFilterKeys, TInputs>> = {};
    for (const input of inputs) {
      if (input.type === "checkbox-group") {
        (clearedInputState as FilterValues<TFilterKeys, TInputs>)[input.name as TFilterKeys] = [] as any;
      } else {
        (clearedInputState as FilterValues<TFilterKeys, TInputs>)[input.name as TFilterKeys] = "" as any;
      }
    }
    setInputValues(clearedInputState as FilterValues<TFilterKeys, TInputs>);

    startTransition(() => {
      replace(pathname); // Navigate to base path
      setIsOpen(false); // Close the drawer
    });
  };

  // Dynamically constructs applied filters for badges
  const appliedFilters = useMemo(() => {
    const filters: Array<{
      key: TFilterKeys;
      value: string | string[];
      label: string;
      config: FilterInputConfig<TFilterKeys>;
    }> = [];

    for (const input of inputs) {
      const urlValue = searchParams.get(input.name);
      if (urlValue) {
        if (input.type === "checkbox-group") {
          const deserialize = input.deserialize || defaultDeserialize;
          const deserialized = deserialize(urlValue);
          if (deserialized.length > 0) {
            filters.push({
              key: input.name,
              value: deserialized,
              label: input.label,
              config: input,
            });
          }
        } else {
          filters.push({
            key: input.name,
            value: urlValue,
            label: input.label,
            config: input,
          });
        }
      }
    }
    return filters;
  }, [searchParams, inputs]);

  // Dynamic `prettyPrintFilter` for badges
  const prettyPrintFilter = (
    key: TFilterKeys,
    value: string | string[],
    config: FilterInputConfig<TFilterKeys>,
  ) => {
    if (config.type === "checkbox-group") {
      const labels = (value as string[])
        .map(
          (v) => config.options.find((opt) => opt.value === v)?.label || v,
        )
        .join(", ");
      return (
        <p>
          {config.label.replace(/%20/g, " ")}: <span className="text-muted-foreground">{labels}</span>
        </p>
      );
    }
    return (
      <p>
        {config.label}: <span className="text-muted-foreground">{value}</span>
      </p>
    );
  };

  // Handles removing an individual filter
  const handleRemoveFilter = (keyToRemove: TFilterKeys) => {
    const newParams = new URLSearchParams(currentParams.toString());
    newParams.delete(keyToRemove);
    newParams.set("page", "1"); // Reset page after removing a filter

    // Clear the specific input's local state
    const inputConfig = inputs.find((input) => input.name === keyToRemove);
    setInputValues((prev) => ({
      ...prev,
      [keyToRemove]: inputConfig?.type === "checkbox-group" ? [] : "",
    }));

    startTransition(() => {
      replace(`${pathname}?${newParams.toString()}`);
    });
  };

  return (
    <div className="flex flex-col items-start justify-start gap-2 w-full">
      <Drawer open={isOpen} onOpenChange={setIsOpen}>
        <DrawerTrigger asChild>
          <Button
            className="w-full sm:w-48 flex items-end justify-between"
            onClick={() => setIsOpen(!isOpen)}
            variant="outline"
          >
            Filter
            <ListFilter />
          </Button>
        </DrawerTrigger>
        <DrawerContent>
          <div className="mx-auto w-full max-w-3xl">
            <DrawerHeader>
              <DrawerTitle>{title}</DrawerTitle>
              <DrawerDescription asChild>
                <div>{description}</div>
              </DrawerDescription>
            </DrawerHeader>

            <div className="grid sm:grid-cols-3 gap-4 p-4">
              {inputs.map((input) => {
                switch (input.type) {
                  case "string":
                  case "number":
                    return (
                      <Input
                        key={input.name}
                        placeholder={input.placeholder}
                        value={inputValues[input.name] as string}
                        onChange={handleTextOrNumberInputChange(input.name)}
                        type={input.type}
                      />
                    );
                  case "checkbox-group":
                    return (
                      <CheckboxGroup
                        key={input.name}
                        name={input.name}
                        label={input.label}
                        options={input.options}
                        selectedValues={inputValues[input.name] as string[]}
                        onChange={handleCheckboxGroupChange(input.name)}
                      />
                    );
                  case "radio-group":
                    return (
                      <div key={input.name} className="flex flex-col gap-2 p-2 border rounded-md">
                        <Label className="font-semibold text-sm">{input.label}</Label>
                        <RadioGroup
                          value={inputValues[input.name] as string}
                          onValueChange={(value) =>
                            setInputValues((prev) => ({
                              ...prev,
                              [input.name]: value,
                            }))
                          }
                        >
                          {input.options.map((option) => (
                            <div key={`${input.name}-${option.value}`} className="flex items-center space-x-2">
                              <RadioGroupItem value={option.value} id={`${input.name}-${option.value}`} />
                              <Label htmlFor={`${input.name}-${option.value}`}>{option.label}</Label>
                            </div>
                          ))}
                        </RadioGroup>
                      </div>
                    );
                  default:
                    return null;
                }
              })}
            </div>
            <DrawerFooter className="max-w-sm mx-auto">
              <Button onClick={handleApplyFilters} disabled={disabled}>
                {disabled ? (
                  <>
                    <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                    Applying...
                  </>
                ) : (
                  <>Apply Filters</>
                )}
              </Button>
              <Button variant="secondary" onClick={handleClearFilters}>
                Clear Filters
              </Button>
              <DrawerClose asChild>
                <Button variant="outline">Cancel</Button>
              </DrawerClose>
            </DrawerFooter>
          </div>
        </DrawerContent>
      </Drawer>
      <div className="flex gap-2 w-fit flex-wrap">
        {appliedFilters.map((filter) => (
          <Badge
            aria-disabled={disabled}
            variant={"outline"}
            className={cn(
              "flex p-2 gap-2 items-center justify-between",
              { "opacity-50 pointer-events-none": disabled },
            )}
            key={filter.key}
          >
            <span className="text-md">
              {prettyPrintFilter(filter.key, filter.value, filter.config)}
            </span>
            {disabled ? (
              <Loader2 className="animate-spin" size={16} />
            ) : (
              <X
                className="bg-sarLinkOrange/50 rounded-full p-1 hover:cursor-pointer hover:ring ring-sarLinkOrange"
                size={16}
                onClick={() => handleRemoveFilter(filter.key)}
              >
                Remove
              </X>
            )}
          </Badge>
        ))}
      </div>
    </div>
  );
}