import {
  useEffect,
  useMemo,
  useRef,
  useState,
  type CSSProperties,
  type KeyboardEvent,
  type PointerEvent,
} from 'react';
import type { BonsaiShellProps } from '../../lib/contracts';
import { normalizeThemeMode, resolveThemeMode, THEME_STORAGE_KEY, type ThemeMode } from '../theme';
import { ChatSurface } from './ChatSurface';
import { ConversationList } from './ConversationList';
import { Header, type ChatWidth } from './Header';
import { InspectorPanel } from './InspectorPanel';
import { ModelRail } from './ModelRail';
import { SettingsSheet } from './SettingsSheet';

const BROWSER_THEME_COLORS = {
  light: '#f3f6f2',
  dark: '#111511',
} as const;

const SIDEBAR_LAYOUT_STORAGE_KEY = 'bonsai-sidebar-layout-v1';
export const SIDEBAR_COLLAPSE_THRESHOLD = 132;
export const SIDEBAR_EXPAND_THRESHOLD = 176;
const MIN_CHAT_WIDTH = 360;
const RESIZE_STEP = 16;

type SidebarSide = 'left' | 'right';

interface SidebarLayout {
  leftWidth: number;
  rightWidth: number;
  leftOpen: boolean;
  rightOpen: boolean;
}

interface SidebarDrag {
  side: SidebarSide;
  pointerId: number;
  startX: number;
  startWidth: number;
  moved: boolean;
}

export function resolveSidebarDrag(rawWidth: number, open: boolean, maxWidth: number) {
  if ((open && rawWidth <= SIDEBAR_COLLAPSE_THRESHOLD)
    || (!open && rawWidth < SIDEBAR_EXPAND_THRESHOLD)) {
    return { open: false, width: 0 } as const;
  }
  return {
    open: true,
    width: Math.round(Math.min(maxWidth, Math.max(SIDEBAR_COLLAPSE_THRESHOLD + 1, rawWidth))),
  } as const;
}

function defaultSidebarLayout(): SidebarLayout {
  const large = (globalThis.innerWidth ?? 0) >= 2400;
  return {
    leftWidth: large ? 340 : 260,
    rightWidth: large ? 420 : 340,
    leftOpen: true,
    rightOpen: true,
  };
}

function readSidebarLayout(): SidebarLayout {
  const fallback = defaultSidebarLayout();
  try {
    const raw = globalThis.localStorage?.getItem(SIDEBAR_LAYOUT_STORAGE_KEY);
    if (!raw) return fallback;
    const stored = JSON.parse(raw) as Partial<SidebarLayout>;
    const normalizeWidth = (value: unknown, defaultWidth: number) => (
      typeof value === 'number' && Number.isFinite(value)
        ? Math.min(800, Math.max(SIDEBAR_EXPAND_THRESHOLD, Math.round(value)))
        : defaultWidth
    );
    return {
      leftWidth: normalizeWidth(stored.leftWidth, fallback.leftWidth),
      rightWidth: normalizeWidth(stored.rightWidth, fallback.rightWidth),
      leftOpen: typeof stored.leftOpen === 'boolean' ? stored.leftOpen : fallback.leftOpen,
      rightOpen: typeof stored.rightOpen === 'boolean' ? stored.rightOpen : fallback.rightOpen,
    };
  } catch {
    return fallback;
  }
}

export function BonsaiShell(props: BonsaiShellProps) {
  const [initialSidebarLayout] = useState(readSidebarLayout);
  const [leftSidebarOpen, setLeftSidebarOpen] = useState(initialSidebarLayout.leftOpen);
  const [rightSidebarOpen, setRightSidebarOpen] = useState(initialSidebarLayout.rightOpen);
  const [leftSidebarWidth, setLeftSidebarWidth] = useState(initialSidebarLayout.leftWidth);
  const [rightSidebarWidth, setRightSidebarWidth] = useState(initialSidebarLayout.rightWidth);
  const [resizingSide, setResizingSide] = useState<SidebarSide | null>(null);
  const [chatWidth, setChatWidth] = useState<ChatWidth>('centered');
  const [themeMode, setThemeMode] = useState<ThemeMode>(() => {
    try {
      return normalizeThemeMode(globalThis.localStorage?.getItem(THEME_STORAGE_KEY));
    } catch {
      return 'system';
    }
  });
  const [systemDark, setSystemDark] = useState(() => globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false);
  const workspaceRef = useRef<HTMLDivElement>(null);
  const dragRef = useRef<SidebarDrag | null>(null);
  const suppressClickRef = useRef<SidebarSide | null>(null);
  const leftOpenRef = useRef(leftSidebarOpen);
  const rightOpenRef = useRef(rightSidebarOpen);
  const leftWidthRef = useRef(leftSidebarWidth);
  const rightWidthRef = useRef(rightSidebarWidth);
  const activeModel = props.models.find((model) => model.id === props.activeModelId);
  const busy = props.streamState === 'loading' || props.streamState === 'streaming';
  const resolvedTheme = resolveThemeMode(themeMode, systemDark);
  const artifacts = useMemo(() => props.messages.flatMap((message) => message.artifacts ?? []), [props.messages]);

  leftOpenRef.current = leftSidebarOpen;
  rightOpenRef.current = rightSidebarOpen;
  leftWidthRef.current = leftSidebarWidth;
  rightWidthRef.current = rightSidebarWidth;

  useEffect(() => {
    const media = globalThis.matchMedia?.('(prefers-color-scheme: dark)');
    if (!media) return;
    const handleChange = (event: MediaQueryListEvent) => setSystemDark(event.matches);
    setSystemDark(media.matches);
    media.addEventListener('change', handleChange);
    return () => media.removeEventListener('change', handleChange);
  }, []);

  useEffect(() => {
    try {
      globalThis.localStorage?.setItem(THEME_STORAGE_KEY, themeMode);
    } catch {
      // Theme still works for the current tab when storage is unavailable.
    }
  }, [themeMode]);

  useEffect(() => {
    const timer = window.setTimeout(() => {
      try {
        globalThis.localStorage?.setItem(SIDEBAR_LAYOUT_STORAGE_KEY, JSON.stringify({
          leftWidth: leftSidebarWidth,
          rightWidth: rightSidebarWidth,
          leftOpen: leftSidebarOpen,
          rightOpen: rightSidebarOpen,
        } satisfies SidebarLayout));
      } catch {
        // Sidebar layout still works for the current tab when storage is unavailable.
      }
    }, 140);
    return () => window.clearTimeout(timer);
  }, [leftSidebarOpen, leftSidebarWidth, rightSidebarOpen, rightSidebarWidth]);

  useEffect(() => {
    const clampToViewport = () => {
      const workspaceWidth = workspaceRef.current?.clientWidth ?? globalThis.innerWidth ?? 0;
      if (workspaceWidth <= 820) return;
      const available = Math.max(SIDEBAR_EXPAND_THRESHOLD * 2, workspaceWidth - MIN_CHAT_WIDTH);
      const left = leftOpenRef.current ? leftWidthRef.current : 0;
      const right = rightOpenRef.current ? rightWidthRef.current : 0;
      if (left + right <= available) return;
      const scale = available / (left + right);
      if (leftOpenRef.current) setLeftSidebarWidth(Math.max(SIDEBAR_EXPAND_THRESHOLD, Math.floor(left * scale)));
      if (rightOpenRef.current) setRightSidebarWidth(Math.max(SIDEBAR_EXPAND_THRESHOLD, Math.floor(right * scale)));
    };
    clampToViewport();
    window.addEventListener('resize', clampToViewport);
    return () => window.removeEventListener('resize', clampToViewport);
  }, []);

  useEffect(() => {
    const themeColor = BROWSER_THEME_COLORS[resolvedTheme];
    const meta = document.querySelector<HTMLMetaElement>('meta[name="theme-color"]');
    const previousMetaColor = meta?.content;
    const previousRootBackground = document.documentElement.style.backgroundColor;
    const previousBodyBackground = document.body.style.backgroundColor;
    const previousColorScheme = document.documentElement.style.colorScheme;
    if (meta) meta.content = themeColor;
    document.documentElement.style.backgroundColor = themeColor;
    document.documentElement.style.colorScheme = resolvedTheme;
    document.body.style.backgroundColor = themeColor;
    return () => {
      if (meta && previousMetaColor !== undefined) meta.content = previousMetaColor;
      document.documentElement.style.backgroundColor = previousRootBackground;
      document.documentElement.style.colorScheme = previousColorScheme;
      document.body.style.backgroundColor = previousBodyBackground;
    };
  }, [resolvedTheme]);

  if (!activeModel) {
    throw new Error(`Active model ${props.activeModelId} is missing from the model catalog`);
  }

  const setSidebarOpen = (side: SidebarSide, open: boolean) => {
    if (side === 'left') {
      leftOpenRef.current = open;
      setLeftSidebarOpen(open);
    } else {
      rightOpenRef.current = open;
      setRightSidebarOpen(open);
    }
  };

  const toggleSidebar = (side: SidebarSide) => {
    setSidebarOpen(side, !(side === 'left' ? leftOpenRef.current : rightOpenRef.current));
  };

  const resizeMaximum = (side: SidebarSide, workspaceWidth: number): number => {
    const large = workspaceWidth >= 2400;
    const hardMaximum = side === 'left' ? (large ? 640 : 520) : (large ? 720 : 600);
    const otherWidth = side === 'left'
      ? (rightOpenRef.current ? rightWidthRef.current : 0)
      : (leftOpenRef.current ? leftWidthRef.current : 0);
    return Math.max(
      SIDEBAR_EXPAND_THRESHOLD,
      Math.min(hardMaximum, workspaceWidth - otherWidth - MIN_CHAT_WIDTH),
    );
  };

  const beginSidebarResize = (side: SidebarSide, event: PointerEvent<HTMLDivElement>) => {
    if (event.pointerType === 'mouse' && event.button !== 0) return;
    event.preventDefault();
    event.currentTarget.setPointerCapture(event.pointerId);
    dragRef.current = {
      side,
      pointerId: event.pointerId,
      startX: event.clientX,
      startWidth: side === 'left' ? leftWidthRef.current : rightWidthRef.current,
      moved: false,
    };
    setResizingSide(side);
    document.body.style.cursor = 'col-resize';
    document.body.style.userSelect = 'none';
  };

  const moveSidebarResize = (event: PointerEvent<HTMLDivElement>) => {
    const drag = dragRef.current;
    const workspace = workspaceRef.current;
    if (!drag || drag.pointerId !== event.pointerId || !workspace) return;
    event.preventDefault();
    if (Math.abs(event.clientX - drag.startX) > 2) drag.moved = true;
    const rect = workspace.getBoundingClientRect();
    const rawWidth = drag.side === 'left'
      ? event.clientX - rect.left
      : rect.right - event.clientX;
    const currentlyOpen = drag.side === 'left' ? leftOpenRef.current : rightOpenRef.current;
    const next = resolveSidebarDrag(rawWidth, currentlyOpen, resizeMaximum(drag.side, rect.width));
    setSidebarOpen(drag.side, next.open);
    if (next.open) {
      if (drag.side === 'left') {
        leftWidthRef.current = next.width;
        setLeftSidebarWidth(next.width);
      } else {
        rightWidthRef.current = next.width;
        setRightSidebarWidth(next.width);
      }
    }
  };

  const endSidebarResize = (event: PointerEvent<HTMLDivElement>) => {
    const drag = dragRef.current;
    if (!drag || drag.pointerId !== event.pointerId) return;
    if (event.currentTarget.hasPointerCapture(event.pointerId)) {
      event.currentTarget.releasePointerCapture(event.pointerId);
    }
    const remainsOpen = drag.side === 'left' ? leftOpenRef.current : rightOpenRef.current;
    if (!remainsOpen) {
      if (drag.side === 'left') setLeftSidebarWidth(drag.startWidth);
      else setRightSidebarWidth(drag.startWidth);
    }
    if (drag.moved) {
      suppressClickRef.current = drag.side;
      window.setTimeout(() => {
        if (suppressClickRef.current === drag.side) suppressClickRef.current = null;
      }, 0);
    }
    dragRef.current = null;
    setResizingSide(null);
    document.body.style.cursor = '';
    document.body.style.userSelect = '';
  };

  const handleResizerClick = (side: SidebarSide) => {
    if (suppressClickRef.current === side) {
      suppressClickRef.current = null;
      return;
    }
    toggleSidebar(side);
  };

  const handleResizerKeyDown = (side: SidebarSide, event: KeyboardEvent<HTMLDivElement>) => {
    if (event.key === 'Enter' || event.key === ' ') {
      event.preventDefault();
      toggleSidebar(side);
      return;
    }
    if (event.key === 'Home') {
      event.preventDefault();
      setSidebarOpen(side, false);
      return;
    }
    if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
    event.preventDefault();
    const expanding = side === 'left' ? event.key === 'ArrowRight' : event.key === 'ArrowLeft';
    const open = side === 'left' ? leftOpenRef.current : rightOpenRef.current;
    if (!open) {
      if (expanding) setSidebarOpen(side, true);
      return;
    }
    const currentWidth = side === 'left' ? leftWidthRef.current : rightWidthRef.current;
    const workspaceWidth = workspaceRef.current?.clientWidth ?? globalThis.innerWidth ?? 0;
    const nextWidth = Math.min(
      resizeMaximum(side, workspaceWidth),
      currentWidth + (expanding ? RESIZE_STEP : -RESIZE_STEP),
    );
    if (nextWidth <= SIDEBAR_COLLAPSE_THRESHOLD) {
      setSidebarOpen(side, false);
    } else if (side === 'left') {
      setLeftSidebarWidth(nextWidth);
    } else {
      setRightSidebarWidth(nextWidth);
    }
  };

  const scrollToArtifact = (artifactId: string) => {
    const artifact = document.getElementById(`artifact-${artifactId}`);
    if (!artifact) return;
    const reducedMotion = globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
    artifact.scrollIntoView({ behavior: reducedMotion ? 'auto' : 'smooth', block: 'center' });
    artifact.focus({ preventScroll: true });
  };

  const workspaceStyle = {
    '--model-rail-width': leftSidebarOpen ? `${leftSidebarWidth}px` : '0px',
    '--inspector-width': rightSidebarOpen ? `${rightSidebarWidth}px` : '0px',
  } as CSSProperties;

  return (
    <div className="station-frame" data-theme={resolvedTheme} data-theme-mode={themeMode}>
      <Header
        onOpenSettings={props.onOpenSettings}
        disabled={busy}
        leftSidebarOpen={leftSidebarOpen}
        rightSidebarOpen={rightSidebarOpen}
        chatWidth={chatWidth}
        onToggleLeftSidebar={() => toggleSidebar('left')}
        onToggleRightSidebar={() => toggleSidebar('right')}
        onToggleChatWidth={() => setChatWidth((width) => width === 'centered' ? 'wide' : 'centered')}
      />
      <div
        ref={workspaceRef}
        className={`workspace-grid layout-${chatWidth}${leftSidebarOpen ? '' : ' is-models-collapsed'}${rightSidebarOpen ? '' : ' is-inspector-collapsed'}${resizingSide ? ' is-resizing' : ''}`}
        style={workspaceStyle}
      >
        <ModelRail
          hidden={!leftSidebarOpen}
          models={props.models}
          activeModelId={props.activeModelId}
          busy={busy}
          onModelSelect={props.onModelSelect}
        />
        {props.conversationListOpen ? <ConversationList
          conversations={props.conversations}
          activeConversationId={props.activeConversationId}
          onNewConversation={props.onNewConversation}
          onOpenConversation={props.onOpenConversation}
          onOpenConversationSettings={props.onOpenConversationSettings}
          onDeleteConversation={props.onDeleteConversation}
        /> : <ChatSurface
          activeModel={activeModel}
          loadedModelId={props.loadedModelId}
          messages={props.messages}
          streamState={props.streamState}
          modelLoadProgress={props.modelLoadProgress}
          toolsEnabled={props.toolsEnabled}
          runtimeStatus={props.runtimeStatus}
          error={props.error}
          shardRetry={props.shardRetry}
          theme={resolvedTheme}
          onLoadModel={props.onLoadModel}
          onDismissError={props.onDismissError}
          onRetryShard={props.onRetryShard}
          onSend={props.onSend}
          onRegenerate={props.onRegenerate}
          onEditUserMessage={props.onEditUserMessage}
          onCancel={props.onCancel}
          onToolsEnabledChange={props.onToolsEnabledChange}
          onOpenConversationList={props.onOpenConversationList}
          onOpenSettings={props.onOpenSettings}
        />}
        <InspectorPanel
          hidden={!rightSidebarOpen}
          telemetry={props.telemetry}
          toolEvents={props.toolEvents}
          artifacts={artifacts}
          onSelectArtifact={scrollToArtifact}
        />
        <SidebarResizer
          side="left"
          open={leftSidebarOpen}
          width={leftSidebarWidth}
          onPointerDown={(event) => beginSidebarResize('left', event)}
          onPointerMove={moveSidebarResize}
          onPointerUp={endSidebarResize}
          onPointerCancel={endSidebarResize}
          onClick={() => handleResizerClick('left')}
          onKeyDown={(event) => handleResizerKeyDown('left', event)}
        />
        <SidebarResizer
          side="right"
          open={rightSidebarOpen}
          width={rightSidebarWidth}
          onPointerDown={(event) => beginSidebarResize('right', event)}
          onPointerMove={moveSidebarResize}
          onPointerUp={endSidebarResize}
          onPointerCancel={endSidebarResize}
          onClick={() => handleResizerClick('right')}
          onKeyDown={(event) => handleResizerKeyDown('right', event)}
        />
      </div>
      <SettingsSheet
        open={props.settingsOpen}
        themeMode={themeMode}
        onThemeModeChange={setThemeMode}
        activeModel={activeModel}
        telemetry={props.telemetry}
        settings={props.settings}
        onSettingsChange={props.onSettingsChange}
        onPersistStorage={props.onPersistStorage}
        modelLoaded={props.loadedModelId !== null}
        onUnloadModel={props.onUnloadModel}
        onClearStorage={props.onClearStorage}
        onClearConversation={props.onClearConversation}
        onClose={props.onCloseSettings}
      />
    </div>
  );
}

interface SidebarResizerProps {
  side: SidebarSide;
  open: boolean;
  width: number;
  onPointerDown: (event: PointerEvent<HTMLDivElement>) => void;
  onPointerMove: (event: PointerEvent<HTMLDivElement>) => void;
  onPointerUp: (event: PointerEvent<HTMLDivElement>) => void;
  onPointerCancel: (event: PointerEvent<HTMLDivElement>) => void;
  onClick: () => void;
  onKeyDown: (event: KeyboardEvent<HTMLDivElement>) => void;
}

function SidebarResizer({
  side,
  open,
  width,
  onPointerDown,
  onPointerMove,
  onPointerUp,
  onPointerCancel,
  onClick,
  onKeyDown,
}: SidebarResizerProps) {
  const label = `${open ? 'Resize or collapse' : 'Expand'} ${side === 'left' ? 'model' : 'telemetry'} sidebar`;
  const arrow = side === 'left' ? (open ? '‹' : '›') : (open ? '›' : '‹');
  return (
    <div
      className={`sidebar-resizer sidebar-resizer-${side}${open ? '' : ' is-collapsed'}`}
      role="separator"
      aria-label={label}
      aria-orientation="vertical"
      aria-valuemin={0}
      aria-valuemax={800}
      aria-valuenow={open ? width : 0}
      tabIndex={0}
      title={label}
      onPointerDown={onPointerDown}
      onPointerMove={onPointerMove}
      onPointerUp={onPointerUp}
      onPointerCancel={onPointerCancel}
      onClick={onClick}
      onKeyDown={onKeyDown}
    >
      <span className="sidebar-resizer-line" aria-hidden="true" />
      <span className="sidebar-resizer-tab" aria-hidden="true">{arrow}</span>
    </div>
  );
}
