"use client";

import { useEffect, useId, useRef, useState } from "react";
import Icon from "@/components/ui/Icon";
import { useCitiesStore } from "@/store/cities";

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

/** Custom dropdown (button + listbox) so the open list matches the design system. Arrow keys, Home/End, Enter, Escape and click-outside all work. */
export function Select({ label, value, options, onChange, disabled }: { label: string; value: string; options: Option[]; onChange: (v: string) => void; disabled?: boolean }) {
  const [open, setOpen] = useState(false);
  const [active, setActive] = useState(0);
  const root = useRef<HTMLDivElement>(null);
  const id = useId();
  const current = options.findIndex((o) => o.value === value);

  useEffect(() => {
    if (!open) return;
    const away = (e: PointerEvent) => !root.current?.contains(e.target as Node) && setOpen(false);
    document.addEventListener("pointerdown", away);
    return () => document.removeEventListener("pointerdown", away);
  }, [open]);

  useEffect(() => {
    if (open) document.getElementById(`${id}-${active}`)?.scrollIntoView({ block: "nearest" });
  }, [open, active, id]);

  const show = () => {
    setActive(Math.max(current, 0));
    setOpen(true);
  };
  const pick = (i: number) => {
    onChange(options[i].value);
    setOpen(false);
  };
  const onKey = (e: React.KeyboardEvent) => {
    if (e.key === "Escape" || e.key === "Tab") return setOpen(false);
    const move = e.key === "ArrowDown" ? 1 : e.key === "ArrowUp" ? -1 : 0;
    const handled = move || e.key === "Home" || e.key === "End" || e.key === "Enter" || e.key === " ";
    if (!handled) return;
    e.preventDefault();
    if (!open) return show();
    if (move) setActive((a) => Math.min(options.length - 1, Math.max(0, a + move)));
    else if (e.key === "Home") setActive(0);
    else if (e.key === "End") setActive(options.length - 1);
    else pick(active);
  };

  return (
    <div className="lf-field" ref={root}>
      <span className="lf-label" id={`${id}-l`}>
        {label}
      </span>
      <div className="lf-dd">
        <button
          type="button"
          className="lf-select"
          role="combobox"
          aria-haspopup="listbox"
          aria-expanded={open}
          aria-controls={`${id}-list`}
          aria-labelledby={`${id}-l`}
          aria-activedescendant={open ? `${id}-${active}` : undefined}
          disabled={disabled}
          onClick={() => (open ? setOpen(false) : show())}
          onKeyDown={onKey}
        >
          <span>{options[current]?.label ?? options[0]?.label}</span>
          <Icon name="expand_more" />
        </button>
        {open && (
          <ul className="lf-menu" data-lenis-prevent role="listbox" id={`${id}-list`} aria-labelledby={`${id}-l`}>
            {options.map((o, i) => (
              <li key={o.value} id={`${id}-${i}`} role="option" aria-selected={i === current} data-active={i === active || undefined} onPointerEnter={() => setActive(i)} onClick={() => pick(i)}>
                {o.label}
                {i === current && <Icon name="check" />}
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}

/** City picker fed by the cities endpoint. Loads once on first render. */
export function CitySelect({ value, onChange, disabled }: { value: number | undefined; onChange: (id: number | undefined) => void; disabled?: boolean }) {
  const cities = useCitiesStore((s) => s.items);
  const status = useCitiesStore((s) => s.status);
  const load = useCitiesStore((s) => s.load);

  useEffect(() => {
    if (status === "idle") void load();
  }, [status, load]);

  const options = [{ value: "", label: "All cities" }, ...cities.map((c) => ({ value: String(c.id), label: c.name }))];
  return <Select label="City" value={value ? String(value) : ""} options={options} disabled={disabled} onChange={(v) => onChange(v ? Number(v) : undefined)} />;
}

/** Search box. Keeps a local draft and writes to the store after 300ms of no typing. */
export function SearchField({ value, onChange, placeholder, disabled }: { value: string; onChange: (q: string) => void; placeholder: string; disabled?: boolean }) {
  const [draft, setDraft] = useState(value);

  // An outside reset (for example "Clear filters") replaces the draft.
  useEffect(() => setDraft(value), [value]);

  useEffect(() => {
    if (draft === value) return;
    const t = window.setTimeout(() => onChange(draft.trim()), 300);
    return () => window.clearTimeout(t);
  }, [draft, value, onChange]);

  return (
    <label className="lf-field lf-field-search">
      <span className="lf-label">Search</span>
      <input type="search" className="lf-search" value={draft} placeholder={placeholder} disabled={disabled} onChange={(e) => setDraft(e.target.value)} />
    </label>
  );
}

/** Filter chip: a pill button whose pressed state is exposed with aria-pressed. */
export function Chip({ pressed, onClick, children, disabled }: { pressed: boolean; onClick: () => void; children: React.ReactNode; disabled?: boolean }) {
  return (
    <button type="button" className="lf-chip" aria-pressed={pressed} onClick={onClick} disabled={disabled}>
      {children}
    </button>
  );
}
