/**
 * Mobile-adapted ticket picker for the web workspace (ticket #86): a full-screen
 * panel — never a small centered desktop modal — with a fixed header, search at
 * the top, a scrollable result list, and actions pinned at the bottom. Reused
 * for both "ajouter des tickets à un sprint" (multi-select) and "lier un
 * ticket" (single-select), same as the desktop `TicketPicker` reuses across
 * those two call sites.
 *
 * Business logic (search/pagination) comes straight from the transport-neutral
 * `useTicketSearch` hook; only the chrome is web-specific.
 */

import { useEffect, useRef } from "react";
import { useState } from "react";

import type { TicketRef } from "@/domain";
import { useTicketSearch, type TicketPickerResult } from "@/features/tickets";
import { Button, Input, Spinner, cn, zIndex } from "@/shared";
import { WebPriorityBadge, WebStatusBadge } from "./webTicketLabels";

export type WebTicketPickerSelectionMode = "single" | "multi";

export interface WebTicketPickerSheetProps {
  projectId: string;
  title: string;
  /** Multi-select only; the count is appended automatically. */
  confirmLabel?: string;
  selectionMode: WebTicketPickerSelectionMode;
  excludeRefs?: TicketRef[];
  onSelect: (result: TicketPickerResult | TicketPickerResult[]) => void;
  onClose: () => void;
}

export function WebTicketPickerSheet({
  projectId,
  title,
  confirmLabel = "Ajouter",
  selectionMode,
  excludeRefs,
  onSelect,
  onClose,
}: WebTicketPickerSheetProps) {
  const vm = useTicketSearch(projectId, { excludeRefs });
  const isMulti = selectionMode === "multi";
  const [selected, setSelected] = useState<Set<TicketRef>>(() => new Set());
  const [busy, setBusy] = useState(false);
  const [resolveError, setResolveError] = useState<string | null>(null);
  const closeRef = useRef<HTMLButtonElement>(null);

  useEffect(() => {
    closeRef.current?.focus();
  }, []);

  useEffect(() => {
    function onKeyDown(e: KeyboardEvent) {
      if (e.key === "Escape") onClose();
    }
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [onClose]);

  function describeErr(e: unknown): string {
    return e && typeof e === "object" && "message" in e
      ? String((e as { message: unknown }).message)
      : String(e);
  }

  function toggle(ref: TicketRef) {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(ref)) next.delete(ref);
      else next.add(ref);
      return next;
    });
  }

  async function pickSingle(ref: TicketRef) {
    if (busy) return;
    setBusy(true);
    setResolveError(null);
    try {
      onSelect(await vm.resolve(ref));
      onClose();
    } catch (e) {
      setResolveError(describeErr(e));
      setBusy(false);
    }
  }

  async function confirmMulti() {
    if (busy || selected.size === 0) return;
    setBusy(true);
    setResolveError(null);
    try {
      const results = await Promise.all(Array.from(selected).map((ref) => vm.resolve(ref)));
      onSelect(results);
      onClose();
    } catch (e) {
      setResolveError(describeErr(e));
      setBusy(false);
    }
  }

  return (
    <div
      className="fixed inset-0 flex flex-col bg-canvas"
      role="dialog"
      aria-modal="true"
      aria-label={title}
      style={{ zIndex: zIndex.floatingWindowNested }}
    >
      <header className="flex shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-3 pt-[max(0.75rem,env(safe-area-inset-top))]">
        <span className="min-w-0 truncate text-sm font-medium text-content">{title}</span>
        <Button ref={closeRef} size="sm" variant="ghost" onClick={onClose}>
          Fermer
        </Button>
      </header>

      <div className="shrink-0 border-b border-border px-4 py-3">
        <Input
          aria-label="Rechercher des tickets"
          placeholder="Recherche…"
          value={vm.text}
          onChange={(e) => vm.setText(e.target.value)}
          autoFocus
        />
      </div>

      {(vm.error || resolveError) && (
        <p role="alert" className="shrink-0 border-b border-danger/40 bg-danger/10 px-4 py-2 text-sm text-danger">
          {resolveError ?? vm.error}
        </p>
      )}

      <div className="min-h-0 flex-1 overflow-auto px-4 py-3">
        {vm.busy && vm.rows.length === 0 ? (
          <div className="flex items-center gap-2 text-sm text-muted">
            <Spinner size={14} />
            <span>Chargement des tickets…</span>
          </div>
        ) : vm.rows.length === 0 ? (
          <p className="text-sm text-muted">Aucun ticket ne correspond.</p>
        ) : (
          <ul className="flex flex-col divide-y divide-border">
            {vm.rows.map((t) => {
              const checked = selected.has(t.ref);
              return (
                <li key={t.ref} className="py-1.5 first:pt-0 last:pb-0">
                  <button
                    type="button"
                    aria-label={`${t.ref}, ${t.title}`}
                    {...(isMulti ? { role: "checkbox", "aria-checked": checked } : {})}
                    disabled={busy}
                    onClick={() => (isMulti ? toggle(t.ref) : void pickSingle(t.ref))}
                    className={cn(
                      "flex min-h-[40px] w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors",
                      "hover:bg-raised focus:bg-raised focus:outline-none disabled:opacity-60",
                      isMulti && checked && "bg-raised",
                    )}
                  >
                    {isMulti && (
                      <span
                        aria-hidden="true"
                        className={cn(
                          "flex h-4 w-4 shrink-0 items-center justify-center rounded border text-[10px] font-bold",
                          checked ? "border-primary bg-primary text-on-primary" : "border-border text-transparent",
                        )}
                      >
                        ✓
                      </span>
                    )}
                    <code className="shrink-0 rounded bg-raised px-1.5 py-0.5 font-mono text-xs text-content">
                      {t.ref}
                    </code>
                    <span className="min-w-0 flex-1 truncate text-sm text-content">{t.title}</span>
                    <span className="flex shrink-0 items-center gap-1">
                      <WebStatusBadge status={t.status} />
                      <WebPriorityBadge priority={t.priority} />
                    </span>
                  </button>
                </li>
              );
            })}
          </ul>
        )}
        {vm.hasMore && (
          <div className="mt-3 flex justify-center">
            <Button size="sm" variant="ghost" loading={vm.busy} onClick={() => vm.loadMore()}>
              Charger plus
            </Button>
          </div>
        )}
      </div>

      {isMulti && (
        <footer className="flex shrink-0 items-center justify-between gap-2 border-t border-border px-4 py-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]">
          <span className="text-xs text-muted" aria-live="polite">
            {selected.size} sélectionné{selected.size > 1 ? "s" : ""}
          </span>
          <Button size="sm" loading={busy} disabled={selected.size === 0 || busy} onClick={() => void confirmMulti()}>
            {confirmLabel} {selected.size > 0 ? `(${selected.size})` : ""}
          </Button>
        </footer>
      )}
    </div>
  );
}
