import { useEffect, useId, useMemo, useRef, useState } from "react";
import { Check, ChevronDown, Search, X } from "lucide-react";
import "./MultiSelect.css";

export interface SelectOption {
  value: string;
  label: string;
  depth?: number;
}

interface MultiSelectProps {
  label: string;
  options: SelectOption[];
  value: string[];
  onChange: (value: string[]) => void;
  placeholder?: string;
}

export function MultiSelect({
  label,
  options,
  value,
  onChange,
  placeholder = "Search categories…",
}: MultiSelectProps) {
  const id = useId();
  const rootRef = useRef<HTMLDivElement>(null);
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState("");

  const selected = useMemo(
    () => options.filter((o) => value.includes(o.value)),
    [options, value],
  );

  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return options;
    // Keep matches and their ancestors so hierarchy stays readable while searching
    const matched = new Set(
      options.filter((o) => o.label.toLowerCase().includes(q)).map((o) => o.value),
    );
    if (matched.size === 0) return [];

    const indexByValue = new Map(options.map((o, i) => [o.value, i]));
    const keep = new Set<string>(matched);

    for (const option of options) {
      if (!matched.has(option.value)) continue;
      const depth = option.depth ?? 0;
      if (depth === 0) continue;
      const idx = indexByValue.get(option.value) ?? 0;
      for (let i = idx - 1; i >= 0; i -= 1) {
        const ancestor = options[i];
        if ((ancestor.depth ?? 0) < depth) {
          keep.add(ancestor.value);
          if ((ancestor.depth ?? 0) === 0) break;
        }
      }
    }

    return options.filter((o) => keep.has(o.value));
  }, [options, query]);

  useEffect(() => {
    function onDocClick(e: MouseEvent) {
      if (!rootRef.current?.contains(e.target as Node)) {
        setOpen(false);
      }
    }
    document.addEventListener("mousedown", onDocClick);
    return () => document.removeEventListener("mousedown", onDocClick);
  }, []);

  function toggle(optionValue: string) {
    if (value.includes(optionValue)) {
      onChange(value.filter((v) => v !== optionValue));
    } else {
      onChange([...value, optionValue]);
    }
  }

  function remove(optionValue: string) {
    onChange(value.filter((v) => v !== optionValue));
  }

  return (
    <div className="field" ref={rootRef}>
      <label className="field__label" htmlFor={id}>
        {label}
      </label>
      <div className={`multiselect${open ? " multiselect--open" : ""}`}>
        <button
          type="button"
          id={id}
          className="multiselect__control"
          onClick={() => setOpen((v) => !v)}
          aria-expanded={open}
          aria-haspopup="listbox"
        >
          <div className="multiselect__chips">
            {selected.length === 0 ? (
              <span className="multiselect__placeholder">Select categories</span>
            ) : (
              selected.map((item) => (
                <span key={item.value} className="multiselect__chip">
                  {item.label}
                  <span
                    role="button"
                    tabIndex={0}
                    className="multiselect__chip-remove"
                    aria-label={`Remove ${item.label}`}
                    onClick={(e) => {
                      e.stopPropagation();
                      remove(item.value);
                    }}
                    onKeyDown={(e) => {
                      if (e.key === "Enter" || e.key === " ") {
                        e.preventDefault();
                        e.stopPropagation();
                        remove(item.value);
                      }
                    }}
                  >
                    <X size={12} />
                  </span>
                </span>
              ))
            )}
          </div>
          <ChevronDown size={16} className="multiselect__chevron" />
        </button>

        {open ? (
          <div className="multiselect__dropdown" role="listbox" aria-multiselectable>
            <div className="multiselect__search">
              <Search size={14} />
              <input
                type="search"
                value={query}
                onChange={(e) => setQuery(e.target.value)}
                placeholder={placeholder}
                autoFocus
              />
            </div>
            <ul className="multiselect__list">
              {filtered.length === 0 ? (
                <li className="multiselect__empty">No categories found</li>
              ) : (
                filtered.map((option) => {
                  const active = value.includes(option.value);
                  const depth = option.depth ?? 0;
                  return (
                    <li key={option.value}>
                      <button
                        type="button"
                        role="option"
                        aria-selected={active}
                        className={`multiselect__option${active ? " is-selected" : ""}`}
                        onClick={() => toggle(option.value)}
                      >
                        <span className="multiselect__option-main">
                          {depth > 0 ? (
                            <span
                              className="multiselect__tree"
                              aria-hidden
                              style={{ width: `${depth * 1.35}rem` }}
                            >
                              {Array.from({ length: depth }, (_, level) => (
                                <span
                                  key={level}
                                  className={`multiselect__tree-col${level === depth - 1 ? " is-branch" : ""}`}
                                />
                              ))}
                            </span>
                          ) : null}
                          <span className="multiselect__option-label">
                            {option.label}
                          </span>
                        </span>
                        {active ? <Check size={14} /> : null}
                      </button>
                    </li>
                  );
                })
              )}
            </ul>
          </div>
        ) : null}
      </div>
    </div>
  );
}
