"use client";

/**
 * PticaDrawer — controlled right-slider detail drawer (CoStar drill-down pattern,
 * per ui-conventions.md: 480px desktop, full-screen on phone).
 *
 * Behaviour ported from the prototype `openDrawer`/`closeDrawer` (app.js):
 *  - scrim click closes,
 *  - Escape closes,
 *  - the close button is focused on open (a11y),
 *  - body scroll is locked while open.
 *
 * Accessible-dialog hardening (a11y follow-up):
 *  - focus-restore: the element focused before the drawer opened (the trigger)
 *    is captured on open and re-focused on close/unmount,
 *  - focus-trap: Tab / Shift+Tab wrap inside the dialog so keyboard focus can
 *    never escape behind the scrim. Focusables are re-queried on each Tab so the
 *    trap survives drawer-content changes (lazy panels, sub-tabs).
 *
 * `role="dialog"` + `aria-modal` + `aria-label` make it a proper modal surface.
 * The component is fully controlled — `open` drives mount/visibility, the host
 * (PticaPageContent) owns the open-key state and URL deep-link.
 */

import { useEffect, useRef } from "react";

import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css";

interface Props {
  open: boolean;
  title: string;
  /** Sub-title under the drawer title (e.g. "Полные данные ЕГРН / НСПД"). */
  sub?: string;
  onClose: () => void;
  children: React.ReactNode;
}

const FOCUSABLE_SELECTOR =
  'a[href], button:not([disabled]), input, select, textarea, [tabindex]:not([tabindex="-1"])';

export function PticaDrawer({ open, title, sub, onClose, children }: Props) {
  const closeRef = useRef<HTMLButtonElement | null>(null);
  const dialogRef = useRef<HTMLElement | null>(null);
  /** Element focused right before the drawer opened — restored on close. */
  const triggerRef = useRef<HTMLElement | null>(null);
  // Hold the latest onClose in a ref so the effect can depend on [open] only.
  // (onClose's identity changes when the host's URL/searchParams change; keeping
  // it out of the deps stops the effect re-running mid-open and thrashing focus.)
  const onCloseRef = useRef(onClose);
  onCloseRef.current = onClose;

  // Esc-to-close + body-scroll-lock + focus the close button on open +
  // focus-trap (Tab/Shift+Tab wrap) + focus-restore to the trigger on close.
  useEffect(() => {
    if (!open) return;

    // Capture the trigger (the element that had focus when the drawer opened)
    // so it can be re-focused on close — standard accessible-dialog behaviour.
    triggerRef.current =
      document.activeElement instanceof HTMLElement
        ? document.activeElement
        : null;

    function onKeyDown(e: KeyboardEvent) {
      if (e.key === "Escape") {
        onCloseRef.current();
        return;
      }
      if (e.key !== "Tab") return;

      const dialog = dialogRef.current;
      if (!dialog) return;

      // Re-query focusables on every Tab so the trap survives content changes
      // (lazy-mounted panels, sub-tabs) rather than caching a stale list.
      const focusables = Array.from(
        dialog.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),
      ).filter((el) => !el.hasAttribute("disabled") && el.tabIndex !== -1);
      if (focusables.length === 0) {
        e.preventDefault();
        return;
      }

      const first = focusables[0];
      const last = focusables[focusables.length - 1];
      const activeEl =
        document.activeElement instanceof HTMLElement
          ? document.activeElement
          : null;

      if (e.shiftKey) {
        // Shift+Tab on the first (or focus already outside) → wrap to last.
        if (activeEl === first || !dialog.contains(activeEl)) {
          e.preventDefault();
          last.focus();
        }
      } else {
        // Tab on the last (or focus already outside) → wrap to first.
        if (activeEl === last || !dialog.contains(activeEl)) {
          e.preventDefault();
          first.focus();
        }
      }
    }
    document.addEventListener("keydown", onKeyDown);

    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";

    // Focus the close button so keyboard users land inside the dialog.
    closeRef.current?.focus();

    return () => {
      document.removeEventListener("keydown", onKeyDown);
      document.body.style.overflow = prevOverflow;

      // Restore focus to the trigger if it is still in the DOM and focusable.
      const trigger = triggerRef.current;
      triggerRef.current = null;
      if (trigger && document.contains(trigger)) {
        trigger.focus();
      }
    };
  }, [open]);

  return (
    <>
      <div
        className={`${styles.drawerScrim} ${open ? styles.drawerScrimOpen : ""}`}
        onClick={onClose}
        aria-hidden="true"
      />
      <aside
        ref={dialogRef}
        className={`${styles.drawer} ${open ? styles.drawerOpen : ""}`}
        role="dialog"
        aria-modal="true"
        aria-label={title}
        aria-hidden={!open}
      >
        <div className={styles.drawerHead}>
          <div>
            <h3 className={styles.drawerTitle}>{title}</h3>
            {sub && <div className={styles.drawerSub}>{sub}</div>}
          </div>
          <button
            ref={closeRef}
            type="button"
            className={styles.drawerClose}
            onClick={onClose}
            aria-label="Закрыть"
          >
            <svg viewBox="0 0 24 24" width="16" height="16" fill="none">
              <path
                d="M6 6l12 12M18 6L6 18"
                stroke="currentColor"
                strokeWidth="1.8"
                strokeLinecap="round"
              />
            </svg>
          </button>
        </div>
        <div className={styles.drawerBody}>{open && children}</div>
      </aside>
    </>
  );
}
