/**
 * Quick Panel Shadow Host
 *
 * Creates an isolated Shadow DOM container for the Quick Panel AI Chat UI.
 * This module runs in a content script context and provides:
 *
 * - Style isolation via Shadow DOM (no CSS bleed in/out)
 * - Event isolation (UI events don't bubble to the host page)
 * - Theme synchronization with AgentChat (via chrome.storage)
 *
 * Architecture:
 * - Host element attached to documentElement with highest z-index
 * - Shadow root contains styles + UI container
 * - Theme is synced from chrome.storage.local['agentTheme']
 */

import { Disposer } from '@/entrypoints/web-editor/utils/disposables';
import { QUICK_PANEL_STYLES } from './styles';

// ============================================================
// Types
// ============================================================

/**
 * Elements exposed by the shadow host for UI mounting.
 */
export interface QuickPanelShadowHostElements {
  /** The host element attached to the document */
  host: HTMLElement;
  /** The shadow root */
  shadowRoot: ShadowRoot;
  /** Container for UI elements (pointer-events: none by default) */
  uiRoot: HTMLElement;
  /** Theme root element (class="agent-theme qp-root") */
  root: HTMLElement;
}

/**
 * Manager interface for the shadow host.
 */
export interface QuickPanelShadowHostManager {
  /** Get the current elements (null if disposed) */
  getElements: () => QuickPanelShadowHostElements | null;
  /** Check if a node belongs to this shadow host */
  isOverlayElement: (node: unknown) => boolean;
  /** Check if an event originated from within the shadow host */
  isEventFromUi: (event: Event) => boolean;
  /** Clean up and remove the shadow host */
  dispose: () => void;
}

/**
 * Options for mounting the shadow host.
 */
export interface QuickPanelShadowHostOptions {
  /** Custom host element ID (default: __mcp_quick_panel_host__) */
  hostId?: string;
  /** Custom z-index (default: 2147483647 - highest possible) */
  zIndex?: number;
}

// ============================================================
// Constants
// ============================================================

const DEFAULT_HOST_ID = '__mcp_quick_panel_host__';
const UI_CONTAINER_ID = '__mcp_quick_panel_ui__';
const ROOT_ID = '__mcp_quick_panel_root__';

/** Highest possible z-index to ensure Quick Panel is on top */
const DEFAULT_Z_INDEX = 2147483647;

/** Storage key for AgentChat theme (owned by sidepanel) */
const THEME_STORAGE_KEY = 'agentTheme';

/** Default theme if none is set */
const DEFAULT_THEME_ID = 'warm-editorial';

/** Dark theme ID for dark mode */
const DARK_THEME_ID = 'dark-console';

/** Valid theme IDs (subset supported by Quick Panel) */
const VALID_THEME_IDS = new Set([
  'warm-editorial',
  'blueprint-architect',
  'zen-journal',
  'neo-pop',
  'dark-console',
  'swiss-grid',
]);

/** Light theme IDs that should switch to dark in dark mode */
const LIGHT_THEME_IDS = new Set([
  'warm-editorial',
  'blueprint-architect',
  'zen-journal',
  'neo-pop',
  'swiss-grid',
]);

/** Events to stop from propagating to the host page */
const BLOCKED_EVENT_TYPES = [
  // Pointer events
  'pointerdown',
  'pointerup',
  'pointermove',
  'pointerenter',
  'pointerleave',
  'pointercancel',
  // Mouse events
  'mousedown',
  'mouseup',
  'mousemove',
  'mouseenter',
  'mouseleave',
  'click',
  'dblclick',
  'contextmenu',
  // Keyboard events
  'keydown',
  'keyup',
  'keypress',
  // Touch events
  'touchstart',
  'touchmove',
  'touchend',
  'touchcancel',
  // Scroll events
  'wheel',
  // Form events
  'focus',
  'blur',
  'input',
  'change',
] as const;

// ============================================================
// Utility Functions
// ============================================================

/**
 * Set a CSS property with !important to override page styles.
 */
function setImportantStyle(element: HTMLElement, property: string, value: string): void {
  element.style.setProperty(property, value, 'important');
}

/**
 * Normalize and validate a theme ID.
 */
function normalizeThemeId(value: unknown): string {
  if (typeof value !== 'string') return DEFAULT_THEME_ID;
  const trimmed = value.trim();
  return VALID_THEME_IDS.has(trimmed) ? trimmed : DEFAULT_THEME_ID;
}

/**
 * Check if system prefers dark mode.
 */
function systemPrefersDark(): boolean {
  try {
    return globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false;
  } catch {
    return false;
  }
}

/**
 * Get effective theme ID considering system dark mode preference.
 * If system is in dark mode and the theme is a light theme, switch to dark-console.
 */
function getEffectiveThemeId(baseThemeId: string): string {
  if (systemPrefersDark() && LIGHT_THEME_IDS.has(baseThemeId)) {
    return DARK_THEME_ID;
  }
  return baseThemeId;
}

/**
 * Read the stored theme ID from chrome.storage.
 */
async function readStoredThemeId(): Promise<string> {
  try {
    if (!chrome?.storage?.local) return DEFAULT_THEME_ID;
    const result = await chrome.storage.local.get(THEME_STORAGE_KEY);
    return normalizeThemeId(result[THEME_STORAGE_KEY]);
  } catch {
    return DEFAULT_THEME_ID;
  }
}

/**
 * Apply a theme ID to the root element, considering system dark mode preference.
 */
function applyThemeId(root: HTMLElement, themeId: string): void {
  const normalizedTheme = normalizeThemeId(themeId);
  const effectiveTheme = getEffectiveThemeId(normalizedTheme);
  root.dataset.agentTheme = effectiveTheme;
}

// ============================================================
// Main Export
// ============================================================

/**
 * Mount the Quick Panel Shadow DOM host.
 *
 * @param options - Configuration options
 * @returns Manager interface for the shadow host
 *
 * @example
 * ```typescript
 * const shadowHost = mountQuickPanelShadowHost();
 * const elements = shadowHost.getElements();
 *
 * if (elements) {
 *   // Mount UI into elements.root
 *   mountQuickPanelAiChatPanel({
 *     mount: elements.root,
 *     agentBridge,
 *   });
 * }
 *
 * // Cleanup when done
 * shadowHost.dispose();
 * ```
 */
export function mountQuickPanelShadowHost(
  options: QuickPanelShadowHostOptions = {},
): QuickPanelShadowHostManager {
  const disposer = new Disposer();
  let elements: QuickPanelShadowHostElements | null = null;

  const hostId = options.hostId ?? DEFAULT_HOST_ID;
  const zIndex = options.zIndex ?? DEFAULT_Z_INDEX;

  // Clean up any existing host (from previous instance or crash recovery)
  const existing = document.getElementById(hostId);
  if (existing) {
    try {
      existing.remove();
    } catch {
      // Best-effort cleanup
    }
  }

  // Create host element
  const host = document.createElement('div');
  host.id = hostId;
  host.setAttribute('data-mcp-quick-panel', 'true');

  // Apply styles with !important to override page styles
  setImportantStyle(host, 'position', 'fixed');
  setImportantStyle(host, 'inset', '0');
  setImportantStyle(host, 'z-index', String(zIndex));
  setImportantStyle(host, 'pointer-events', 'none');
  setImportantStyle(host, 'contain', 'layout style paint');
  setImportantStyle(host, 'isolation', 'isolate');

  // Create shadow root
  const shadowRoot = host.attachShadow({ mode: 'open' });

  // Inject styles
  const styleEl = document.createElement('style');
  styleEl.textContent = QUICK_PANEL_STYLES;
  shadowRoot.append(styleEl);

  // Create UI container
  const uiRoot = document.createElement('div');
  uiRoot.id = UI_CONTAINER_ID;
  setImportantStyle(uiRoot, 'position', 'fixed');
  setImportantStyle(uiRoot, 'inset', '0');
  setImportantStyle(uiRoot, 'pointer-events', 'none');
  shadowRoot.append(uiRoot);

  // Create theme root (where UI components mount)
  const root = document.createElement('div');
  root.id = ROOT_ID;
  root.className = 'agent-theme qp-root';

  // Apply theme synchronously BEFORE mounting to avoid flash
  // Use system dark mode preference as initial hint
  const initialTheme = getEffectiveThemeId(DEFAULT_THEME_ID);
  root.dataset.agentTheme = initialTheme;

  uiRoot.append(root);

  // Mount to document
  const mountPoint = document.documentElement ?? document.body;
  mountPoint.append(host);
  disposer.add(() => host.remove());

  elements = { host, shadowRoot, uiRoot, root };

  // Event isolation: stop UI events from bubbling to the page
  const stopPropagation = (event: Event): void => {
    event.stopPropagation();
  };

  for (const eventType of BLOCKED_EVENT_TYPES) {
    disposer.listen(root, eventType, stopPropagation);
  }

  // Async update with stored theme (if different from initial)
  void (async () => {
    const themeId = await readStoredThemeId();
    applyThemeId(root, themeId);
  })();

  // System dark mode change listener
  // Re-apply theme when system color scheme changes
  let currentStoredThemeId = DEFAULT_THEME_ID;

  // Track the stored theme ID
  void (async () => {
    currentStoredThemeId = await readStoredThemeId();
  })();

  // Theme change listener
  const handleStorageChange = (
    changes: Record<string, chrome.storage.StorageChange>,
    areaName: string,
  ): void => {
    if (areaName !== 'local') return;
    const change = changes[THEME_STORAGE_KEY];
    if (!change) return;
    // Update tracked theme ID and apply
    currentStoredThemeId = normalizeThemeId(change.newValue);
    applyThemeId(root, currentStoredThemeId);
  };

  try {
    chrome?.storage?.onChanged?.addListener(handleStorageChange);
    disposer.add(() => chrome?.storage?.onChanged?.removeListener(handleStorageChange));
  } catch {
    // Best-effort: theme sync is optional
  }

  try {
    const darkModeMediaQuery = globalThis.matchMedia?.('(prefers-color-scheme: dark)');
    if (darkModeMediaQuery) {
      const handleDarkModeChange = (): void => {
        applyThemeId(root, currentStoredThemeId);
      };

      // Use addEventListener for modern browsers
      if (typeof darkModeMediaQuery.addEventListener === 'function') {
        darkModeMediaQuery.addEventListener('change', handleDarkModeChange);
        disposer.add(() => darkModeMediaQuery.removeEventListener('change', handleDarkModeChange));
      }
    }
  } catch {
    // Best-effort: dark mode detection is optional
  }

  // Helper to check if a node belongs to this shadow host
  const isOverlayElement = (node: unknown): boolean => {
    if (!(node instanceof Node)) return false;
    if (node === host) return true;

    const rootNode = typeof node.getRootNode === 'function' ? node.getRootNode() : null;
    return rootNode instanceof ShadowRoot && rootNode.host === host;
  };

  // Helper to check if an event originated from within the shadow host
  const isEventFromUi = (event: Event): boolean => {
    try {
      if (typeof event.composedPath === 'function') {
        return event.composedPath().some((el) => isOverlayElement(el));
      }
    } catch {
      // Fallback to checking target
    }
    return isOverlayElement(event.target);
  };

  return {
    getElements: () => elements,
    isOverlayElement,
    isEventFromUi,
    dispose: () => {
      elements = null;
      disposer.dispose();
    },
  };
}
