'use client';

import { useEffect } from 'react';
import type { CSSProperties, ReactNode } from 'react';

import { Portal } from '@djangocfg/ui-core/components';
import { useIsMobile, useIsTabletOrBelow } from '@djangocfg/ui-core/hooks';
import { cn } from '@djangocfg/ui-core/lib';

import { Z_INDEX } from '../constants';
import { ChatHeader } from './header';
import { useChatPresence } from './useChatPresence';
import type { ChatFABPosition } from './ChatFAB';

export type ChatDockMode = 'popover' | 'side';
export type ChatDockSide = 'left' | 'right';

export interface ChatDockProps {
  /** Controlled open state. */
  open: boolean;
  /** Called when the user clicks the close button. */
  onClose: () => void;
  /** Dock contents (typically a `<Chat>` component). */
  children: ReactNode;
  /**
   * Visual mode.
   * - `popover` (default): floating card anchored to a corner, fixed size, FAB-style.
   * - `side`: docked panel pinned to the left/right edge, full viewport height.
   */
  mode?: ChatDockMode;
  /** Side for `mode='side'`. @default 'right' */
  side?: ChatDockSide;
  /** Header title text. */
  title?: ReactNode;
  /** Header icon. Defaults to a bot glyph. */
  icon?: ReactNode;
  /**
   * Header actions slot (right side, before the close button).
   * Use `ChatHeaderActionButton` to keep visual consistency.
   */
  headerActions?: ReactNode;
  /** Hide the header entirely (you render your own inside `children`). */
  hideHeader?: boolean;
  /** ARIA label for the close button. @default 'Close' */
  closeLabel?: string;
  /** Dock width in px. Clamped to viewport. @default 480 (popover) / 420 (side) */
  width?: number;
  /** Dock height in px. Only used in `popover` mode. @default 720 */
  height?: number;
  /** Which screen corner to dock to in `popover` mode. @default 'bottom-right' */
  position?: ChatFABPosition;
  /** Offset from screen edges in px (popover only). @default 24 / 96 */
  offset?: { horizontal?: number; vertical?: number };
  /** Transition duration in ms — should match CSS animation. @default 200 */
  exitDurationMs?: number;
  /**
   * z-index. @default 100 — page furniture: above page content but below
   * every ui-core overlay (sheet/drawer/dialog/popover), so a dialog always
   * covers the chat, including one opened from inside the chat itself.
   */
  zIndex?: number;
  /** Accessible dialog label. */
  ariaLabel?: string;
  /** Extra classes on the dock container. */
  className?: string;
  /**
   * Take over the full viewport on mobile (< 768px). Applies to both modes.
   * @default true
   */
  mobileFullscreen?: boolean;
  /**
   * Render in-place (not in `document.body` via a portal). Useful for stories,
   * screenshots, or wrapping the dock inside a custom container. @default false
   */
  disablePortal?: boolean;
  /**
   * Drop fixed positioning entirely — the dock renders as a normal flow
   * element sized by `width`/`height`. Combine with `disablePortal` for
   * stories/previews where the dock should sit inside the panel instead
   * of attaching to the viewport. @default false
   */
  inline?: boolean;
  /**
   * In `mode='side'`, reserve space on the document body so page content
   * isn't covered by the dock. Sets `padding-{side}` on `<body>` while
   * the dock is open and exposes the width via the `--chat-dock-reserve`
   * CSS variable for custom layouts. @default true (when mode='side')
   */
  reserveBodySpace?: boolean;
}

function dockPositionStyle(
  position: ChatFABPosition,
  horizontal: number,
  vertical: number,
): CSSProperties {
  const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];
  return { [vert]: vertical, [horiz]: horizontal } as CSSProperties;
}

/**
 * Fixed-position chat surface. Two modes:
 *
 * - `popover` — floating card anchored to a corner. Companion to `<ChatFAB>`.
 * - `side` — full-height panel pinned to the left/right edge. App-shell style.
 *
 * Renders only when `open` is true (plus the leave-transition tail). Uses
 * `useChatPresence` for the four-phase mount/animate/unmount cycle.
 */
export function ChatDock({
  open,
  onClose,
  children,
  mode = 'popover',
  side = 'right',
  title = 'Chat',
  icon,
  headerActions,
  hideHeader = false,
  closeLabel,
  width,
  height = 720,
  position = 'bottom-right',
  offset,
  exitDurationMs = 200,
  zIndex = Z_INDEX.dock,
  ariaLabel,
  className,
  mobileFullscreen = true,
  disablePortal = false,
  inline = false,
  reserveBodySpace,
}: ChatDockProps) {
  const phase = useChatPresence(open, exitDurationMs);
  const isMobile = useIsMobile();
  // Side mode is desktop-only — narrow viewports fall back to popover so
  // we never cover 33% of a phone/tablet with a chat panel.
  const isBelowDesktop = useIsTabletOrBelow();
  const effectiveMode: ChatDockMode =
    mode === 'side' && !isBelowDesktop ? 'side' : 'popover';
  const fullscreen = mobileFullscreen && isMobile;

  // Reserve body padding for side mode so page content stays visible
  // next to the dock. Auto-on when mode='side' unless explicitly disabled.
  const wantsReserve =
    !inline && !fullscreen && effectiveMode === 'side' && (reserveBodySpace ?? true);
  const resolvedSideWidth = width ?? 420;
  useEffect(() => {
    if (!wantsReserve || phase === 'hidden') return;
    const body = document.body;
    if (!body) return;
    const cssVar = `${resolvedSideWidth}px`;
    const padKey = side === 'right' ? 'paddingRight' : 'paddingLeft';
    const prevPad = body.style[padKey as 'paddingRight' | 'paddingLeft'];
    const prevVar = body.style.getPropertyValue('--chat-dock-reserve');
    body.style[padKey as 'paddingRight' | 'paddingLeft'] = cssVar;
    body.style.setProperty('--chat-dock-reserve', cssVar);
    return () => {
      body.style[padKey as 'paddingRight' | 'paddingLeft'] = prevPad;
      if (prevVar) body.style.setProperty('--chat-dock-reserve', prevVar);
      else body.style.removeProperty('--chat-dock-reserve');
    };
  }, [wantsReserve, phase, side, resolvedSideWidth]);

  if (phase === 'hidden') return null;

  const animating = phase === 'entering' || phase === 'leaving';

  const horizontal = offset?.horizontal ?? 24;
  const vertical = offset?.vertical ?? 96;
  const resolvedWidth = width ?? (effectiveMode === 'side' ? resolvedSideWidth : 480);

  let containerStyle: CSSProperties;
  let cornerClass: string;

  // Dynamic viewport heights — `dvh` follows iOS Safari URL bar (preferred),
  // `svh`/`lvh` are the small/large fallbacks if the dynamic value isn't
  // supported. Min-height keeps the popover usable even on tiny landscape phones.
  const dynVH = '100dvh';

  if (inline) {
    containerStyle = {
      position: 'relative',
      width: resolvedWidth,
      height,
      maxHeight: `calc(${dynVH} - 16px)`,
      pointerEvents: phase === 'visible' ? 'auto' : 'none',
    };
    cornerClass = 'rounded-xl border';
  } else if (fullscreen) {
    containerStyle = {
      position: 'fixed',
      top: 0,
      [side === 'left' ? 'left' : 'right']: 0,
      width: '100vw',
      height: dynVH,
      zIndex,
      pointerEvents: phase === 'visible' ? 'auto' : 'none',
    } as CSSProperties;
    cornerClass = 'rounded-none border-0';
  } else if (effectiveMode === 'side') {
    containerStyle = {
      position: 'fixed',
      top: 0,
      [side]: 0,
      height: dynVH,
      zIndex,
      width: `min(${resolvedWidth}px, 100vw)`,
      pointerEvents: phase === 'visible' ? 'auto' : 'none',
    } as CSSProperties;
    cornerClass = side === 'right' ? 'rounded-none border-l' : 'rounded-none border-r';
  } else {
    // popover — anchored to a corner, capped to viewport so it never
    // overlaps the FAB or goes off-screen on small windows.
    const heightCap = `calc(${dynVH} - ${vertical + 24}px)`;
    containerStyle = {
      position: 'fixed',
      ...dockPositionStyle(position, horizontal, vertical),
      zIndex,
      width: `min(${resolvedWidth}px, calc(100vw - 32px))`,
      height: `min(${height}px, ${heightCap})`,
      minHeight: `min(320px, ${heightCap})`,
      pointerEvents: phase === 'visible' ? 'auto' : 'none',
    };
    cornerClass = 'rounded-xl border';
  }

  // Per-mode enter/leave transform classes — side slides in horizontally,
  // popover scales + lifts.
  const enterClass = (() => {
    if (fullscreen) return 'opacity-0';
    if (effectiveMode === 'side') {
      return side === 'right' ? 'opacity-0 translate-x-4' : 'opacity-0 -translate-x-4';
    }
    return 'opacity-0 scale-95 translate-y-2';
  })();
  const visibleClass = 'opacity-100 scale-100 translate-y-0 translate-x-0';

  return (
    <Portal disablePortal={disablePortal || inline}>
      <div
        role="dialog"
        aria-label={ariaLabel ?? (typeof title === 'string' ? title : 'Chat')}
        // While leaving, mark the panel `inert` rather than `aria-hidden`.
        // `aria-hidden` on an ancestor of the still-focused element (e.g.
        // the close button just clicked, or the composer) trips the
        // browser's "Blocked aria-hidden ... descendant retained focus"
        // warning. `inert` hides it from the a11y tree AND blanks focus.
        inert={phase === 'leaving' ? true : undefined}
        className={cn(
          'bg-popover text-popover-foreground border-border',
          'flex flex-col overflow-hidden shadow-2xl',
          cornerClass,
          'transition-all duration-200 ease-out',
          animating ? enterClass : visibleClass,
          className,
        )}
        style={containerStyle}
      >
        {!hideHeader && (
          <ChatHeader
            title={title}
            icon={icon}
            actions={headerActions}
            onClose={onClose}
            closeLabel={closeLabel}
          />
        )}
        <div className="min-h-0 min-w-0 flex-1 overflow-hidden">{children}</div>
      </div>
    </Portal>
  );
}
