import { useCallback, useState, useId, type ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import {
  ArrowLeftToLine,
  ArrowRightToLine,
  History,
  Trash2,
  X,
} from 'lucide-react'
import { Sheet, SheetPortal } from '@/ui'
import * as DialogPrimitive from '@radix-ui/react-dialog'
import { Button } from '@/ui'
import { cn } from '@/lib/utils'

/**
 * RecordDrawer — right slide-over для create/edit записи. Stage 3.5 per
 * design_handoff_ordinis_mdm/README.md (Screen 3).
 *
 * <p>Layout (sticky frame, scrolling body):
 * <pre>
 *   ┌───────────────────────────────────────┐
 *   │ HEADER (sticky)                       │
 *   │  caption · id · [‹/›] [⏱] [×]         │
 *   ├───────────────────────────────────────┤
 *   │ BODY (scroll)                         │
 *   │  children — SchemaDrivenForm          │
 *   ├───────────────────────────────────────┤
 *   │ FOOTER (sticky)                       │
 *   │ [Удалить] · "N полей" · [Отмена][Save]│
 *   └───────────────────────────────────────┘
 * </pre>
 *
 * <p>Two widths:
 * <ul>
 *   <li>520px (default) — focused single-column edit.</li>
 *   <li>880px (wide) — for dense schemas + future ToC rail. Persists в
 *       localStorage key {@code ord-drawer-wide}.</li>
 * </ul>
 *
 * <p>Status (v2.12+):
 * <ul>
 *   <li>✅ Drawer shell + width toggle + sticky chrome (Phase 1).</li>
 *   <li>✅ Toolbar: search полей + «только заполненные» (props.toolbar).</li>
 *   <li>✅ Section anchor chips + scroll-spy active highlight — рендерит
 *       SchemaDrivenForm в sections layout (sticky chip strip с
 *       IntersectionObserver).</li>
 *   <li>❌ Wide-mode left ToC rail — отклонено по user feedback
 *       («в редакторе записей левая навигация лишняя»); chip strip
 *       выполняет ту же функцию quick-jump компактнее.</li>
 * </ul>
 *
 * <p>Drives form submit через атрибут {@code form={formId}} на footer Save
 * button. SchemaDrivenForm должен получить тот же {@code formId} prop и
 * скрыть свой внутренний footer.
 */

const LS_KEY = 'ord-drawer-wide'

/** Hook: persisted boolean (drawer wide mode). */
function useDrawerWide(): [boolean, () => void] {
  const [wide, setWide] = useState<boolean>(() => {
    if (typeof window === 'undefined') return false
    return window.localStorage.getItem(LS_KEY) === '1'
  })
  const toggle = useCallback(() => {
    setWide((prev) => {
      const next = !prev
      try {
        window.localStorage.setItem(LS_KEY, next ? '1' : '0')
      } catch {
        // localStorage может быть disabled (Safari private). Игнорируем —
        // в памяти state сохраняется до закрытия таба, persist опционален.
      }
      return next
    })
  }, [])
  return [wide, toggle]
}

export type RecordDrawerProps = {
  open: boolean
  onClose: () => void
  /** Title caption — "редактирование записи · {dictionary}" or "новая запись · {dictionary}". */
  caption: string
  /** Record business key / id — shown в header mono. Undefined для create. */
  recordId?: string
  /** Save button form attribute target. SchemaDrivenForm renders <form id={formId}>. */
  formId: string
  /** Dirty-field count для caption "не сохранено · N полей". 0 hides it. */
  dirtyCount?: number
  /** Disable footer buttons (mutation в полёте). */
  isPending?: boolean
  /** Show [Удалить] danger button (только edit mode, не create). */
  onDelete?: () => void
  /** Show [История] button + handler (только edit mode). */
  onHistory?: () => void
  /** Optional sticky toolbar per handoff line 279: search полей by name +
   * "только заполненные" toggle + counter. Caller controls state. Pass
   * undefined чтобы скрыть toolbar (e.g. create mode without filter context). */
  toolbar?: {
    search: string
    onSearchChange: (next: string) => void
    onlyFilled: boolean
    onOnlyFilledChange: (next: boolean) => void
    /** "N / M полей" caption — visible / total. */
    counterVisible: number
    counterTotal: number
  }
  children: ReactNode
}

export function RecordDrawer({
  open,
  onClose,
  caption,
  recordId,
  formId,
  dirtyCount = 0,
  isPending = false,
  onDelete,
  onHistory,
  toolbar,
  children,
}: RecordDrawerProps) {
  const { t } = useTranslation()
  const [wide, toggleWide] = useDrawerWide()
  // Уникальный id для DialogTitle/Description чтобы Radix mapping aria-labelledby.
  const titleId = useId()

  // Reset focus к save button при открытии — обычно user закончил поиск и хочет
  // быстро save после check. Radix focus traps inside, default landing — first
  // focusable. Save в footer первой стрелкой Tab был бы удобнее, но это
  // нарушает default flow. Пока default.

  return (
    <Sheet open={open} onOpenChange={(v) => { if (!v) onClose() }}>
      <SheetPortal>
        <DialogPrimitive.Overlay
          className={cn(
            'fixed inset-0 z-50 bg-black/40',
            'data-[state=open]:animate-in data-[state=closed]:animate-out',
            'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
          )}
        />
        <DialogPrimitive.Content
          aria-labelledby={titleId}
          className={cn(
            'fixed inset-y-0 right-0 z-50 h-full bg-surface shadow-2xl border-l border-line',
            'flex flex-col',
            'transition-[max-width] duration-200 ease-out',
            // Mobile <880px: fullscreen per handoff (line 465).
            'w-full',
            wide ? 'min-[880px]:max-w-[880px]' : 'min-[880px]:max-w-[520px]',
            'data-[state=open]:animate-in data-[state=closed]:animate-out',
            'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right',
            'data-[state=closed]:duration-200 data-[state=open]:duration-300',
          )}
        >
          {/* === HEADER (sticky top) === */}
          <header className="shrink-0 border-b border-line-2 px-5 py-3 flex items-center gap-3">
            <div className="min-w-0 flex-1">
              <DialogPrimitive.Title id={titleId} className="text-cap text-mute truncate">
                {caption}
              </DialogPrimitive.Title>
              {/* sr-only Description чтобы Radix не ругался на missing
                * `aria-describedby` (a11y warning). Visible description нам не
                * нужен — caption + recordId уже описывают drawer. */}
              <DialogPrimitive.Description className="sr-only">
                {caption}{recordId ? ` (${recordId})` : ''}
              </DialogPrimitive.Description>
              {recordId && (
                <div className="text-mono text-ink-2 truncate mt-0.5">{recordId}</div>
              )}
            </div>
            <div className="shrink-0 flex items-center gap-1">
              <button
                type="button"
                aria-label={wide ? t('drawer.collapse') : t('drawer.expand')}
                title={wide ? t('drawer.collapse') : t('drawer.expand')}
                onClick={toggleWide}
                className="p-1.5 rounded-sm text-mute hover:text-ink hover:bg-surface-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
              >
                {wide ? <ArrowRightToLine size={16} /> : <ArrowLeftToLine size={16} />}
              </button>
              {onHistory && (
                <button
                  type="button"
                  aria-label={t('history.title')}
                  title={t('history.title')}
                  onClick={onHistory}
                  // Icon-only вариант user'у было незаметно («кнопок не вижу»).
                  // Теперь icon + text label, более prominent в header'е.
                  className="inline-flex items-center gap-1.5 px-2 py-1 rounded-sm text-cap text-ink-2 hover:text-ink hover:bg-surface-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                >
                  <History size={14} />
                  <span>{t('history.title')}</span>
                </button>
              )}
              <DialogPrimitive.Close
                aria-label={t('drawer.close')}
                className="p-1.5 rounded-sm text-mute hover:text-ink hover:bg-surface-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
              >
                <X size={16} strokeWidth={2.25} />
              </DialogPrimitive.Close>
            </div>
          </header>

          {/* === TOOLBAR (sticky) per handoff line 279 ===
            * Optional — caller passes toolbar prop с search/onlyFilled state. */}
          {toolbar && (
            <div className="shrink-0 border-b border-line-2 px-5 py-2 flex items-center gap-3 bg-surface">
              <input
                type="search"
                value={toolbar.search}
                onChange={(e) => toolbar.onSearchChange(e.target.value)}
                placeholder={t('drawer.searchPlaceholder', { defaultValue: 'Поиск полей' })}
                className="flex-1 h-7 px-2.5 rounded-md border border-line bg-surface-2 text-body text-ink placeholder:text-mute focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
              />
              <label className="inline-flex items-center gap-1.5 text-cell text-ink-2 select-none cursor-pointer shrink-0">
                <input
                  type="checkbox"
                  checked={toolbar.onlyFilled}
                  onChange={(e) => toolbar.onOnlyFilledChange(e.target.checked)}
                  className="size-3.5 rounded-sm border-line accent-accent"
                />
                {t('drawer.onlyFilled', { defaultValue: 'только заполненные' })}
              </label>
              <span className="text-mono text-mute tabular-nums shrink-0">
                {toolbar.counterVisible}/{toolbar.counterTotal}
              </span>
            </div>
          )}

          {/* === BODY (scrolling) ===
            * SchemaDrivenForm в sections layout рендерит свой sticky chip
            * strip + IntersectionObserver scroll-spy. Drawer body — просто
            * overflow-y-auto контейнер, scroll root для observer'а. */}
          <div className="flex-1 overflow-y-auto px-5 py-4">{children}</div>

          {/* === FOOTER (sticky bottom) === */}
          <footer className="shrink-0 border-t border-line-2 px-5 py-3 flex items-center gap-3 bg-surface">
            {onDelete && (
              <Button
                type="button"
                variant="danger"
                size="sm"
                onClick={onDelete}
                disabled={isPending}
                className="shrink-0"
              >
                <Trash2 size={14} className="mr-1.5" />
                {t('form.delete')}
              </Button>
            )}
            <div className="flex-1 min-w-0 text-cell text-mute truncate">
              {dirtyCount > 0 && t('form.unsavedNFields', { count: dirtyCount })}
            </div>
            <Button
              type="button"
              variant="ghost"
              size="sm"
              onClick={onClose}
              disabled={isPending}
            >
              {t('form.cancel')}
            </Button>
            <Button
              type="submit"
              form={formId}
              variant="primary"
              size="sm"
              loading={isPending}
            >
              {isPending ? t('form.saving') : t('form.save')}
            </Button>
          </footer>
        </DialogPrimitive.Content>
      </SheetPortal>
    </Sheet>
  )
}
