/* eslint-disable */
// @ts-nocheck
/**
 * Props Agent - MAIN World Script
 *
 * Runtime hacking agent for React props editing.
 * Communicates with ISOLATED world via CustomEvent.
 *
 * Architecture:
 * - Transport: CustomEvent-based request/response
 * - Locator: Simplified ElementLocator resolution
 * - ReactAdapter: DevTools Hook detection/injection + overrideProps
 * - Serializer: Safe Props serialization with type preservation
 * - Handlers: Request operation dispatch
 *
 * @module props-agent
 */
(() => {
  'use strict';

  // =============================================================================
  // Constants & Guards
  // =============================================================================

  const GLOBAL_KEY = '__MCP_WEB_EDITOR_PROPS_AGENT__';
  if (window[GLOBAL_KEY]) return;

  const PROTOCOL_VERSION = 1;
  const LOG_PREFIX = '[PropsAgent]';

  const EVENT_NAME = Object.freeze({
    REQUEST: 'web-editor-props:request',
    RESPONSE: 'web-editor-props:response',
    CLEANUP: 'web-editor-props:cleanup',
  });

  const REACT_HOOK_NAME = '__REACT_DEVTOOLS_GLOBAL_HOOK__';

  /** @type {'READY' | 'HOOK_PRESENT_NO_RENDERERS' | 'RENDERERS_NO_EDITING' | 'HOOK_MISSING'} */
  const HOOK_STATUS = Object.freeze({
    READY: 'READY',
    HOOK_PRESENT_NO_RENDERERS: 'HOOK_PRESENT_NO_RENDERERS',
    RENDERERS_NO_EDITING: 'RENDERERS_NO_EDITING',
    HOOK_MISSING: 'HOOK_MISSING',
  });

  const SERIALIZE_LIMITS = Object.freeze({
    maxDepth: 4,
    maxEntries: 100,
    maxArrayLength: 50,
    maxStringLength: 1500,
  });

  // =============================================================================
  // Utilities
  // =============================================================================

  function isObject(value) {
    return value !== null && typeof value === 'object';
  }

  function safeString(value) {
    try {
      if (typeof value === 'string') return value;
      if (value === null || value === undefined) return '';
      return String(value);
    } catch {
      return '';
    }
  }

  function logWarn(...args) {
    try {
      console.warn(LOG_PREFIX, ...args);
    } catch {
      // Silently ignore
    }
  }

  // =============================================================================
  // Transport Layer
  // =============================================================================

  const Transport = {
    dispatchResponse(detail) {
      try {
        window.dispatchEvent(new CustomEvent(EVENT_NAME.RESPONSE, { detail }));
      } catch (err) {
        logWarn('Failed to dispatch response:', err);
      }
    },

    createResponse(requestId, success, data, error) {
      const response = {
        v: PROTOCOL_VERSION,
        requestId,
        success: Boolean(success),
      };
      if (data !== undefined) response.data = data;
      if (error !== undefined) response.error = safeString(error);
      return response;
    },

    normalizeRequest(detail) {
      if (!isObject(detail)) return null;
      if (detail.v !== PROTOCOL_VERSION) return null;

      const requestId = typeof detail.requestId === 'string' ? detail.requestId : '';
      const op = typeof detail.op === 'string' ? detail.op : '';
      if (!requestId || !op) return null;

      return {
        v: PROTOCOL_VERSION,
        requestId,
        op,
        locator: detail.locator,
        payload: detail.payload,
      };
    },
  };

  // =============================================================================
  // Locator - Element Resolution
  // =============================================================================

  const Locator = {
    safeQuerySelector(root, selector) {
      try {
        if (!root || typeof selector !== 'string' || !selector.trim()) return null;
        return root.querySelector(selector);
      } catch {
        return null;
      }
    },

    safeQuerySelectorAll(root, selector) {
      try {
        if (!root || typeof selector !== 'string' || !selector.trim()) return [];
        return Array.from(root.querySelectorAll(selector));
      } catch {
        return [];
      }
    },

    isSelectorUnique(root, selector) {
      return this.safeQuerySelectorAll(root, selector).length === 1;
    },

    computeFingerprint(element) {
      try {
        const parts = [];
        const tag = element?.tagName ? String(element.tagName).toLowerCase() : 'unknown';
        parts.push(tag);
        const id = element?.id ? String(element.id).trim() : '';
        if (id) parts.push(`id=${id}`);
        return parts.join('|');
      } catch {
        return '';
      }
    },

    verifyFingerprint(element, fingerprint) {
      try {
        const current = this.computeFingerprint(element);
        const storedParts = safeString(fingerprint).split('|');
        const currentParts = current.split('|');

        // Tag must match
        if (storedParts[0] !== currentParts[0]) return false;

        // If stored has id, current must have same id
        const storedId = storedParts.find((p) => p.startsWith('id='));
        const currentId = currentParts.find((p) => p.startsWith('id='));
        if (storedId && storedId !== currentId) return false;

        return true;
      } catch {
        return false;
      }
    },

    normalizeStringArray(value) {
      if (!Array.isArray(value)) return [];
      return value.map((v) => safeString(v).trim()).filter(Boolean);
    },

    /**
     * Resolve ElementLocator to DOM element
     * Simplified version for MAIN world (no iframe support yet)
     */
    locate(locator, rootDocument = document) {
      try {
        if (!isObject(locator)) return null;

        let queryRoot = rootDocument;

        // Traverse Shadow DOM host chain
        const shadowHostChain = this.normalizeStringArray(locator.shadowHostChain);
        for (const hostSelector of shadowHostChain) {
          if (!this.isSelectorUnique(queryRoot, hostSelector)) return null;
          const host = this.safeQuerySelector(queryRoot, hostSelector);
          if (!host) return null;
          const shadowRoot = host.shadowRoot;
          if (!shadowRoot) return null;
          queryRoot = shadowRoot;
        }

        // Try each selector candidate
        const selectors = this.normalizeStringArray(locator.selectors);
        for (const selector of selectors) {
          if (!this.isSelectorUnique(queryRoot, selector)) continue;
          const element = this.safeQuerySelector(queryRoot, selector);
          if (!element) continue;

          // Verify fingerprint if provided
          const fp = safeString(locator.fingerprint);
          if (fp && !this.verifyFingerprint(element, fp)) continue;

          return element;
        }
      } catch {
        // Best-effort
      }
      return null;
    },
  };

  // =============================================================================
  // React Adapter
  // =============================================================================

  const ReactAdapter = {
    /** Store original values for reset (fiber -> { renderer, originals: Map }) */
    overrideStore: typeof WeakMap === 'function' ? new WeakMap() : null,

    /** Flag to avoid repeated hook installation attempts */
    hookInstallAttempted: false,

    getHook() {
      try {
        return window[REACT_HOOK_NAME] || null;
      } catch {
        return null;
      }
    },

    /**
     * Install minimal DevTools hook if missing.
     * Note: This only helps if React hasn't initialized yet.
     * Only attempts once per session to avoid repeated pollution.
     */
    installMinimalHook() {
      // Only attempt once per session
      if (this.hookInstallAttempted) {
        return { installed: false, hook: this.getHook(), skipped: true };
      }
      this.hookInstallAttempted = true;
      try {
        const existing = window[REACT_HOOK_NAME];
        if (existing && typeof existing.inject === 'function') {
          return { installed: false, hook: existing };
        }

        const listeners = Object.create(null);

        const hook = {
          renderers: new Map(),
          supportsFiber: true,

          inject(renderer) {
            try {
              const id = this.renderers.size + 1;
              this.renderers.set(id, renderer);
              this.emit('renderer', { id, renderer });
              return id;
            } catch {
              return 0;
            }
          },

          // Required lifecycle callbacks (no-ops)
          onCommitFiberRoot() {},
          onCommitFiberUnmount() {},
          onPostCommitFiberRoot() {},
          setStrictMode() {},
          checkDCE() {},

          // Event emitter
          on(event, fn) {
            if (typeof event !== 'string' || typeof fn !== 'function') return;
            if (!listeners[event]) listeners[event] = new Set();
            listeners[event].add(fn);
          },

          off(event, fn) {
            if (typeof event !== 'string' || typeof fn !== 'function') return;
            listeners[event]?.delete(fn);
          },

          emit(event, data) {
            const set = listeners[event];
            if (!set) return;
            for (const fn of Array.from(set)) {
              try {
                fn(data);
              } catch {
                // Listener errors must not break the hook
              }
            }
          },

          sub(event, fn) {
            this.on(event, fn);
            return () => this.off(event, fn);
          },
        };

        window[REACT_HOOK_NAME] = hook;
        return { installed: true, hook };
      } catch (err) {
        return { installed: false, hook: null, error: err };
      }
    },

    /**
     * Normalize hook.renderers to array format
     */
    normalizeRenderers(hook) {
      const result = [];
      if (!hook) return result;

      try {
        const renderers = hook.renderers;
        if (renderers instanceof Map) {
          for (const [id, renderer] of renderers.entries()) {
            result.push({ id, renderer });
          }
        } else if (renderers && typeof renderers === 'object') {
          for (const [id, renderer] of Object.entries(renderers)) {
            result.push({ id, renderer });
          }
        }
      } catch {
        // Best-effort
      }
      return result;
    },

    /**
     * Detect Hook status (4 states)
     */
    detectStatus() {
      const hook = this.getHook();

      if (!hook || typeof hook.inject !== 'function') {
        return {
          hookStatus: HOOK_STATUS.HOOK_MISSING,
          hook: null,
          renderers: [],
          editableRenderers: [],
        };
      }

      const renderers = this.normalizeRenderers(hook);
      if (!renderers.length) {
        return {
          hookStatus: HOOK_STATUS.HOOK_PRESENT_NO_RENDERERS,
          hook,
          renderers,
          editableRenderers: [],
        };
      }

      const editableRenderers = renderers.filter(
        (r) => r?.renderer && typeof r.renderer.overrideProps === 'function',
      );

      if (editableRenderers.length) {
        return {
          hookStatus: HOOK_STATUS.READY,
          hook,
          renderers,
          editableRenderers,
        };
      }

      return {
        hookStatus: HOOK_STATUS.RENDERERS_NO_EDITING,
        hook,
        renderers,
        editableRenderers: [],
      };
    },

    /**
     * Get React version from renderer or global.
     * Prioritizes specific renderer version for multi-renderer scenarios.
     *
     * @param {object} hookInfo - Result from detectStatus()
     * @param {object} [specificRenderer] - Specific renderer to prefer (from resolveFiberWithRenderer)
     * @returns {string | undefined}
     */
    getVersion(hookInfo, specificRenderer) {
      try {
        // Priority 1: Specific renderer version (for multi-renderer scenarios)
        if (specificRenderer) {
          const version = specificRenderer.version;
          if (typeof version === 'string' && version.trim()) {
            return version.trim();
          }
        }

        // Priority 2: Any renderer with version
        const renderers = hookInfo?.renderers || [];
        for (const item of renderers) {
          const version = item?.renderer?.version;
          if (typeof version === 'string' && version.trim()) {
            return version.trim();
          }
        }

        // Priority 3: Global React object (if exposed)
        if (typeof window !== 'undefined' && window.React?.version) {
          return String(window.React.version).trim();
        }
      } catch {
        // Best-effort
      }
      return undefined;
    },

    /**
     * Find React fiber from DOM node
     */
    findFiberFromDOM(node) {
      try {
        if (!node || typeof node !== 'object') return null;
        const keys = Object.keys(node);
        for (const key of keys) {
          if (key.startsWith('__reactFiber$') || key.startsWith('__reactInternalInstance$')) {
            return node[key];
          }
        }
      } catch {
        // Best-effort
      }
      return null;
    },

    /**
     * Check if fiber tag is a component (Function/Class/ForwardRef etc.)
     */
    isComponentTag(tag) {
      // 0=FunctionComponent, 1=ClassComponent, 2=IndeterminateComponent,
      // 11=ForwardRef, 14=MemoComponent, 15=SimpleMemoComponent
      return tag === 0 || tag === 1 || tag === 2 || tag === 11 || tag === 14 || tag === 15;
    },

    /**
     * Find nearest component fiber by walking up the fiber tree
     */
    findNearestComponentFiber(fiber) {
      try {
        let current = fiber;
        for (let i = 0; i < 60 && current; i++) {
          if (this.isComponentTag(current.tag)) return current;
          current = current.return;
        }
      } catch {
        // Best-effort
      }
      return null;
    },

    /**
     * Get component display name from fiber
     */
    getComponentName(fiber) {
      try {
        const type = fiber?.type || fiber?.elementType;
        if (!type) return 'Anonymous';
        if (typeof type === 'string') return type;
        return safeString(type.displayName || type.name) || 'Anonymous';
      } catch {
        return 'Anonymous';
      }
    },

    /**
     * Extract debug source from React Fiber.
     * Walks up the fiber tree checking _debugSource and _debugOwner._debugSource.
     *
     * @param {object} fiber - React Fiber node
     * @returns {{ file: string, line?: number, column?: number, componentName?: string } | null}
     */
    getDebugSource(fiber) {
      try {
        let current = fiber;
        for (let i = 0; i < 40 && current; i++) {
          if (!isObject(current)) break;

          // Try direct _debugSource first
          const src = isObject(current._debugSource) ? current._debugSource : null;
          if (src) {
            const file = safeString(src.fileName).trim();
            if (file) {
              return this.buildDebugSourceResult(file, src.lineNumber, src.columnNumber, current);
            }
          }

          // Fallback to _debugOwner._debugSource
          const owner = isObject(current._debugOwner) ? current._debugOwner : null;
          const ownerSrc = owner && isObject(owner._debugSource) ? owner._debugSource : null;
          if (ownerSrc) {
            const ownerFile = safeString(ownerSrc.fileName).trim();
            if (ownerFile) {
              return this.buildDebugSourceResult(
                ownerFile,
                ownerSrc.lineNumber,
                ownerSrc.columnNumber,
                owner,
              );
            }
          }

          current = current.return;
        }
      } catch {
        // Best-effort extraction
      }
      return null;
    },

    /**
     * Build debug source result with validated line/column values.
     * @private
     */
    buildDebugSourceResult(file, lineNumber, columnNumber, fiberForName) {
      const line = Number(lineNumber);
      const column = Number(columnNumber);
      return {
        file,
        line: Number.isFinite(line) && line > 0 ? line : undefined,
        column: Number.isFinite(column) && column > 0 ? column : undefined,
        componentName: this.getComponentName(fiberForName),
      };
    },

    /**
     * Resolve fiber using renderer.findFiberByHostInstance when available
     */
    resolveFiberWithRenderer(element, hookInfo) {
      // Prefer renderer API (returns renderer-owned fiber suitable for overrideProps)
      try {
        const renderers = hookInfo?.renderers || [];
        for (const item of renderers) {
          const renderer = item?.renderer;
          if (!renderer || typeof renderer.findFiberByHostInstance !== 'function') continue;
          try {
            const fiber = renderer.findFiberByHostInstance(element);
            if (fiber) return { fiber, renderer };
          } catch {
            // Try next renderer
          }
        }
      } catch {
        // Best-effort
      }

      // Fallback: DOM-attached fiber reference
      const fallback = this.findFiberFromDOM(element);
      return { fiber: fallback, renderer: null };
    },

    /**
     * Record original value for reset
     */
    recordOriginal(fiber, renderer, path, existed, value) {
      if (!this.overrideStore || !fiber) return;

      try {
        const key = JSON.stringify(path);
        let store = this.overrideStore.get(fiber);

        if (!store) {
          store = { renderer: renderer || null, originals: new Map() };
          this.overrideStore.set(fiber, store);

          // Also store by alternate to improve reset hit rate
          if (fiber.alternate && typeof fiber.alternate === 'object') {
            this.overrideStore.set(fiber.alternate, store);
          }
        }

        if (!store.originals.has(key)) {
          store.originals.set(key, { path, existed, value });
        }

        if (!store.renderer && renderer) {
          store.renderer = renderer;
        }
      } catch {
        // Best-effort
      }
    },

    /**
     * Get stored originals for fiber
     */
    getOriginals(fiber) {
      if (!this.overrideStore || !fiber) return null;
      return this.overrideStore.get(fiber) || null;
    },

    /**
     * Clear stored originals for fiber
     */
    clearOriginals(fiber) {
      if (!this.overrideStore || !fiber) return;
      const store = this.overrideStore.get(fiber);
      if (store?.originals) store.originals.clear();
    },
  };

  // =============================================================================
  // Framework Detector
  // =============================================================================

  const FrameworkDetector = {
    /**
     * Detect framework for element (walks up DOM tree)
     */
    detect(element, maxDepth = 15) {
      let node = element;

      for (let depth = 0; depth < maxDepth && node; depth++) {
        // React first (more common)
        const fiber = ReactAdapter.findFiberFromDOM(node);
        if (fiber) {
          return { framework: 'react', node, data: fiber };
        }

        node = node.parentElement;
      }

      return { framework: 'unknown', node: null, data: null };
    },
  };

  // =============================================================================
  // Serializer
  // =============================================================================

  const Serializer = {
    /**
     * Check if value is a React element
     */
    isReactElement(value) {
      try {
        if (!value || typeof value !== 'object') return false;
        const t = value.$$typeof;
        if (!t) return false;

        if (typeof Symbol === 'function' && Symbol.for) {
          return (
            t === Symbol.for('react.element') ||
            t === Symbol.for('react.transitional.element') ||
            t === Symbol.for('react.portal')
          );
        }

        // Fallback heuristic
        return !!(value.type && value.props);
      } catch {
        return false;
      }
    },

    /**
     * Get React element display string
     */
    reactElementDisplay(value) {
      try {
        const type = value?.type;
        if (typeof type === 'string') return `<${type} />`;
        if (typeof type === 'function') {
          return `<${safeString(type.displayName || type.name) || 'Anonymous'} />`;
        }
        if (type && typeof type === 'object') {
          const name = safeString(type.displayName || type.name) || 'Anonymous';
          return `<${name} />`;
        }
      } catch {
        // ignore
      }
      return '<ReactElement />';
    },

    /**
     * Check if value is an editable primitive
     */
    isEditablePrimitive(value) {
      if (value === null || value === undefined) return true;
      const t = typeof value;
      if (t === 'string' || t === 'boolean') return true;
      if (t === 'number') return Number.isFinite(value);
      return false;
    },

    /**
     * Create serialization context for cycle detection
     */
    createContext() {
      return {
        seen: typeof WeakMap === 'function' ? new WeakMap() : null,
        nextId: 1,
      };
    },

    /**
     * Serialize a value with type information
     */
    serializeValue(value, ctx, depth = 0) {
      try {
        if (value === null) return { kind: 'null' };
        if (value === undefined) return { kind: 'undefined' };

        const t = typeof value;

        if (t === 'string') {
          if (value.length > SERIALIZE_LIMITS.maxStringLength) {
            return {
              kind: 'string',
              value: value.slice(0, SERIALIZE_LIMITS.maxStringLength),
              truncated: true,
              length: value.length,
            };
          }
          return { kind: 'string', value };
        }

        if (t === 'number') {
          if (Number.isFinite(value)) return { kind: 'number', value };
          if (Number.isNaN(value)) return { kind: 'number', special: 'NaN' };
          return { kind: 'number', special: value > 0 ? 'Infinity' : '-Infinity' };
        }

        if (t === 'boolean') return { kind: 'boolean', value };
        if (t === 'bigint') return { kind: 'bigint', value: value.toString() };
        if (t === 'symbol') return { kind: 'symbol', description: safeString(value) };
        if (t === 'function')
          return { kind: 'function', name: safeString(value.name) || undefined };

        // Object types
        if (this.isReactElement(value)) {
          return { kind: 'react_element', display: this.reactElementDisplay(value) };
        }

        if (typeof Element !== 'undefined' && value instanceof Element) {
          return {
            kind: 'dom_element',
            tagName: safeString(value.tagName).toLowerCase(),
            id: safeString(value.id) || undefined,
            className: safeString(value.className) || undefined,
          };
        }

        if (value instanceof Date) {
          let iso = '';
          try {
            iso = value.toISOString();
          } catch {
            iso = safeString(value);
          }
          return { kind: 'date', value: iso };
        }

        if (value instanceof RegExp) {
          return { kind: 'regexp', source: value.source, flags: value.flags };
        }

        if (value instanceof Error) {
          return {
            kind: 'error',
            name: safeString(value.name) || 'Error',
            message: safeString(value.message),
          };
        }

        // Depth limit
        if (depth >= SERIALIZE_LIMITS.maxDepth) {
          return {
            kind: 'max_depth',
            type: Object.prototype.toString.call(value),
            preview: safeString(value),
          };
        }

        // Circular reference detection
        if (ctx?.seen) {
          const existingId = ctx.seen.get(value);
          if (existingId) return { kind: 'circular', refId: existingId };
          ctx.seen.set(value, ctx.nextId++);
        }

        // Array
        if (Array.isArray(value)) {
          const max = Math.min(value.length, SERIALIZE_LIMITS.maxArrayLength);
          const items = [];
          for (let i = 0; i < max; i++) {
            items.push(this.serializeValue(value[i], ctx, depth + 1));
          }
          return {
            kind: 'array',
            length: value.length,
            truncated: value.length > max,
            items,
          };
        }

        // Map
        if (value instanceof Map) {
          const entries = [];
          let count = 0;
          for (const [k, v] of value.entries()) {
            if (count >= SERIALIZE_LIMITS.maxEntries) break;
            entries.push({
              key: this.serializeValue(k, ctx, depth + 1),
              value: this.serializeValue(v, ctx, depth + 1),
            });
            count++;
          }
          return {
            kind: 'map',
            size: value.size,
            truncated: value.size > count,
            entries,
          };
        }

        // Set
        if (value instanceof Set) {
          const items = [];
          let count = 0;
          for (const v of value.values()) {
            if (count >= SERIALIZE_LIMITS.maxEntries) break;
            items.push(this.serializeValue(v, ctx, depth + 1));
            count++;
          }
          return {
            kind: 'set',
            size: value.size,
            truncated: value.size > count,
            items,
          };
        }

        // Plain object
        const constructorName = value?.constructor?.name;
        const name = typeof constructorName === 'string' ? constructorName : undefined;
        const keys = Object.keys(value);
        const limitedKeys = keys.slice(0, SERIALIZE_LIMITS.maxEntries);
        const entries = limitedKeys.map((k) => ({
          key: k,
          value: this.serializeValue(value[k], ctx, depth + 1),
        }));

        return {
          kind: 'object',
          name: name !== 'Object' ? name : undefined,
          truncated: keys.length > limitedKeys.length,
          entries,
        };
      } catch (err) {
        return { kind: 'unknown', type: typeof value, preview: safeString(err) };
      }
    },

    /**
     * Serialize props object to structured format
     * @param {object} props - Props object to serialize
     * @param {Record<string, Array<string|number|boolean>>} [enumValuesByKey] - Optional enum values by prop key
     */
    serializeProps(props, enumValuesByKey) {
      const ctx = this.createContext();
      const entries = [];
      const enumMap = isObject(enumValuesByKey) ? enumValuesByKey : null;

      if (!props || (typeof props !== 'object' && typeof props !== 'function')) {
        return { kind: 'props', entries: [] };
      }

      const keys = Object.keys(props);
      const limited = keys.slice(0, SERIALIZE_LIMITS.maxEntries);

      for (const key of limited) {
        let raw;
        try {
          raw = props[key];
        } catch {
          raw = undefined;
        }

        const entry = {
          key,
          editable: this.isEditablePrimitive(raw),
          value: this.serializeValue(raw, ctx, 0),
        };

        // Attach enum values if available
        const enumValues = enumMap ? enumMap[key] : null;
        if (Array.isArray(enumValues) && enumValues.length > 0) {
          entry.enumValues = enumValues.slice(0, EnumIntrospection.MAX_ENUM_VALUES);
        }

        entries.push(entry);
      }

      const result = { kind: 'props', entries };
      if (keys.length > limited.length) result.truncated = true;
      return result;
    },
  };

  // =============================================================================
  // Enum Introspection (Best-effort)
  // =============================================================================

  /**
   * Best-effort enum value extraction from React runtime metadata.
   *
   * React: Relies on __docgenInfo (Storybook/react-docgen output)
   */
  const EnumIntrospection = {
    MAX_ENUM_VALUES: 50,

    /**
     * Normalize a raw enum value to primitive
     */
    normalizeEnumValue(raw) {
      if (raw === null || raw === undefined) return null;

      if (typeof raw === 'boolean') return raw;
      if (typeof raw === 'number') return Number.isFinite(raw) ? raw : null;

      const s = safeString(raw).trim();
      if (!s) return null;

      // Strip surrounding quotes: "'primary'" -> "primary"
      const m = s.match(/^(['"])(.*)\1$/);
      const unquoted = m ? m[2] : s;

      if (unquoted === 'true') return true;
      if (unquoted === 'false') return false;

      if (/^-?(?:\d+|\d*\.\d+)$/.test(unquoted)) {
        const n = Number(unquoted);
        if (Number.isFinite(n)) return n;
      }

      return unquoted;
    },

    /**
     * Normalize array of enum values, deduplicate
     */
    normalizeEnumList(list) {
      if (!Array.isArray(list)) return [];
      const out = [];
      const seen = new Set();

      for (const item of list) {
        const v = this.normalizeEnumValue(item);
        if (v === null) continue;
        const key =
          typeof v === 'string' ? `s:${v}` : typeof v === 'number' ? `n:${v}` : `b:${v ? 1 : 0}`;
        if (seen.has(key)) continue;
        seen.add(key);
        out.push(v);
        if (out.length >= this.MAX_ENUM_VALUES) break;
      }

      return out;
    },

    /**
     * Extract enum values from React docgen prop info
     * (e.g., from Storybook's __docgenInfo)
     */
    extractDocgenEnumValues(propInfo) {
      if (!isObject(propInfo)) return [];

      // Check type.name === 'enum' with type.value array
      const t = propInfo.type;
      if (isObject(t) && t.name === 'enum' && Array.isArray(t.value)) {
        const rawList = t.value.map((item) =>
          isObject(item) && 'value' in item ? item.value : item,
        );
        return this.normalizeEnumList(rawList);
      }

      // Check tsType for TypeScript enums
      const ts = propInfo.tsType;
      if (isObject(ts) && ts.name === 'union' && Array.isArray(ts.elements)) {
        const rawList = ts.elements.map((el) =>
          isObject(el) && 'value' in el ? el.value : el.name,
        );
        return this.normalizeEnumList(rawList);
      }

      return [];
    },

    /**
     * Get enum values map for React component
     */
    getReactEnumValues(componentFiber) {
      try {
        const type = componentFiber?.type || componentFiber?.elementType;
        if (!type) return {};

        const docgen = type.__docgenInfo;
        if (!isObject(docgen) || !isObject(docgen.props)) return {};

        const result = {};
        for (const [key, info] of Object.entries(docgen.props)) {
          const values = this.extractDocgenEnumValues(info);
          if (values.length > 0) result[key] = values;
        }
        return result;
      } catch {
        return {};
      }
    },

  };

  // =============================================================================
  // Value Access Helpers
  // =============================================================================

  function getValueAtPath(root, path) {
    let current = root;

    for (let i = 0; i < path.length; i++) {
      const seg = path[i];
      if (!isObject(current) && !Array.isArray(current)) {
        return { ok: false, existed: false, value: undefined };
      }

      const has = Object.prototype.hasOwnProperty.call(current, seg);
      current = current[seg];

      if (!has && i === path.length - 1) {
        return { ok: true, existed: false, value: undefined };
      }
    }

    return { ok: true, existed: true, value: current };
  }

  // Dangerous keys that could cause prototype pollution or unexpected behavior
  const DANGEROUS_KEYS = new Set([
    '__proto__',
    'constructor',
    'prototype',
    '__defineGetter__',
    '__defineSetter__',
    '__lookupGetter__',
    '__lookupSetter__',
  ]);

  function isDangerousKey(key) {
    return typeof key === 'string' && DANGEROUS_KEYS.has(key);
  }

  function normalizePropPath(value) {
    if (!Array.isArray(value) || value.length === 0 || value.length > 32) return null;

    const result = [];
    for (const seg of value) {
      if (typeof seg === 'string') {
        const s = seg.trim();
        if (!s) return null;
        // Reject dangerous keys to prevent prototype pollution
        if (isDangerousKey(s)) return null;
        result.push(s);
      } else if (typeof seg === 'number' && Number.isInteger(seg) && seg >= 0 && seg <= 1e6) {
        result.push(seg);
      } else {
        return null;
      }
    }
    return result;
  }

  function decodeIncomingValue(raw) {
    // Bridge encodes undefined as { $we: 'undefined' }
    if (isObject(raw) && raw.$we === 'undefined') return undefined;
    return raw;
  }

  // =============================================================================
  // Capabilities Builder
  // =============================================================================

  function makeCapabilities(init) {
    return {
      canRead: Boolean(init?.canRead),
      canWrite: Boolean(init?.canWrite),
      canWriteHooks: Boolean(init?.canWriteHooks),
    };
  }

  function buildResponseData(init) {
    const data = {};
    if (init?.hookStatus) data.hookStatus = init.hookStatus;
    if (typeof init?.needsRefresh === 'boolean') data.needsRefresh = init.needsRefresh;
    if (init?.framework) data.framework = init.framework;
    if (init?.frameworkVersion) data.frameworkVersion = init.frameworkVersion;
    if (init?.componentName) data.componentName = init.componentName;
    if (init?.debugSource) data.debugSource = init.debugSource;
    if (init?.props) data.props = init.props;
    if (init?.capabilities) data.capabilities = init.capabilities;
    if (init?.meta) data.meta = init.meta;
    return data;
  }

  // =============================================================================
  // Request Handlers
  // =============================================================================

  const Handlers = {
    resolveTarget(locator) {
      if (!locator) return null;
      const el = Locator.locate(locator, document);
      // Return element if connected to DOM; otherwise return null
      return el?.isConnected ? el : null;
    },

    /**
     * Handle 'probe' operation - Detect capabilities without reading props
     */
    handleProbe(req) {
      // Check initial hook status
      const preStatus = ReactAdapter.detectStatus();
      const initialHookStatus = preStatus.hookStatus;

      // Try to install hook if missing (only helps if React hasn't initialized)
      if (initialHookStatus === HOOK_STATUS.HOOK_MISSING) {
        ReactAdapter.installMinimalHook();
      }

      const hookInfo = ReactAdapter.detectStatus();
      // Report original status if hook was missing (so UI knows refresh is needed)
      const hookStatus =
        initialHookStatus === HOOK_STATUS.HOOK_MISSING
          ? HOOK_STATUS.HOOK_MISSING
          : hookInfo.hookStatus;

      const target = this.resolveTarget(req.locator);
      const fw = target ? FrameworkDetector.detect(target) : { framework: 'unknown', data: null };

      let componentName;
      let debugSource;
      let canRead = false;
      let canWrite = false;
      let needsRefresh = false;

      let frameworkVersion;

      if (fw.framework === 'react') {
        const fiberInfo = ReactAdapter.resolveFiberWithRenderer(target, hookInfo);
        const componentFiber = fiberInfo.fiber
          ? ReactAdapter.findNearestComponentFiber(fiberInfo.fiber)
          : null;

        componentName = componentFiber ? ReactAdapter.getComponentName(componentFiber) : undefined;
        // Extract debug source from component fiber or raw fiber
        const sourceFiber = componentFiber || fiberInfo.fiber;
        debugSource = sourceFiber ? ReactAdapter.getDebugSource(sourceFiber) : undefined;
        // Pass specific renderer to prioritize its version in multi-renderer scenarios
        frameworkVersion = ReactAdapter.getVersion(hookInfo, fiberInfo.renderer);
        canRead = Boolean(componentFiber);
        canWrite = hookStatus === HOOK_STATUS.READY && Boolean(componentFiber);
        needsRefresh = canRead && hookStatus !== HOOK_STATUS.READY;
      }

      const data = buildResponseData({
        hookStatus,
        framework: fw.framework,
        frameworkVersion,
        componentName,
        debugSource,
        capabilities: makeCapabilities({ canRead, canWrite, canWriteHooks: false }),
        needsRefresh,
      });

      return Transport.createResponse(req.requestId, true, data);
    },

    /**
     * Handle 'read' operation - Read component props
     */
    handleRead(req) {
      const target = this.resolveTarget(req.locator);
      if (!target) {
        return Transport.createResponse(
          req.requestId,
          false,
          undefined,
          'Target element not found',
        );
      }

      const preStatus = ReactAdapter.detectStatus();
      if (preStatus.hookStatus === HOOK_STATUS.HOOK_MISSING) {
        ReactAdapter.installMinimalHook();
      }

      const hookInfo = ReactAdapter.detectStatus();
      const hookStatus =
        preStatus.hookStatus === HOOK_STATUS.HOOK_MISSING
          ? HOOK_STATUS.HOOK_MISSING
          : hookInfo.hookStatus;

      const fw = FrameworkDetector.detect(target);

      if (fw.framework === 'react') {
        const fiberInfo = ReactAdapter.resolveFiberWithRenderer(target, hookInfo);
        const componentFiber = fiberInfo.fiber
          ? ReactAdapter.findNearestComponentFiber(fiberInfo.fiber)
          : null;

        // Extract debug source even if component fiber not found
        const sourceFiber = componentFiber || fiberInfo.fiber;
        const debugSource = sourceFiber ? ReactAdapter.getDebugSource(sourceFiber) : undefined;
        // Pass specific renderer to prioritize its version in multi-renderer scenarios
        const frameworkVersion = ReactAdapter.getVersion(hookInfo, fiberInfo.renderer);

        if (!componentFiber) {
          const data = buildResponseData({
            hookStatus,
            framework: 'react',
            frameworkVersion,
            debugSource,
            capabilities: makeCapabilities({ canRead: false, canWrite: false }),
            needsRefresh: false,
          });
          return Transport.createResponse(
            req.requestId,
            false,
            data,
            'React component fiber not found',
          );
        }

        const props = componentFiber.memoizedProps;
        const enumValuesByKey = EnumIntrospection.getReactEnumValues(componentFiber);
        const serialized = Serializer.serializeProps(props, enumValuesByKey);
        const componentName = ReactAdapter.getComponentName(componentFiber);
        const canWrite = hookStatus === HOOK_STATUS.READY;
        const needsRefresh = hookStatus !== HOOK_STATUS.READY;

        const data = buildResponseData({
          hookStatus,
          framework: 'react',
          frameworkVersion,
          componentName,
          debugSource,
          props: serialized,
          capabilities: makeCapabilities({ canRead: true, canWrite, canWriteHooks: false }),
          needsRefresh,
        });

        return Transport.createResponse(req.requestId, true, data);
      }

      // Unknown framework
      const data = buildResponseData({
        hookStatus,
        framework: 'unknown',
        capabilities: makeCapabilities({ canRead: false, canWrite: false }),
        needsRefresh: false,
      });

      return Transport.createResponse(req.requestId, false, data, 'Not a React component');
    },

    /**
     * Handle 'write' operation - Modify component props
     */
    handleWrite(req) {
      const target = this.resolveTarget(req.locator);
      if (!target) {
        return Transport.createResponse(
          req.requestId,
          false,
          undefined,
          'Target element not found',
        );
      }

      const path = normalizePropPath(req.payload?.propPath);
      if (!path) {
        return Transport.createResponse(req.requestId, false, undefined, 'Invalid propPath');
      }

      const rawValue = req.payload?.propValue;
      const value = decodeIncomingValue(rawValue);
      if (!Serializer.isEditablePrimitive(value)) {
        return Transport.createResponse(
          req.requestId,
          false,
          undefined,
          'Only primitive prop values are supported',
        );
      }

      const preStatus = ReactAdapter.detectStatus();
      if (preStatus.hookStatus === HOOK_STATUS.HOOK_MISSING) {
        ReactAdapter.installMinimalHook();
      }

      const hookInfo = ReactAdapter.detectStatus();
      const hookStatus =
        preStatus.hookStatus === HOOK_STATUS.HOOK_MISSING
          ? HOOK_STATUS.HOOK_MISSING
          : hookInfo.hookStatus;

      const fw = FrameworkDetector.detect(target);

      if (fw.framework === 'react') {
        const fiberInfo = ReactAdapter.resolveFiberWithRenderer(target, hookInfo);
        const componentFiber = fiberInfo.fiber
          ? ReactAdapter.findNearestComponentFiber(fiberInfo.fiber)
          : null;

        const componentName = componentFiber
          ? ReactAdapter.getComponentName(componentFiber)
          : undefined;
        const canRead = Boolean(componentFiber);
        const canWrite = hookStatus === HOOK_STATUS.READY && Boolean(componentFiber);
        const needsRefresh = canRead && hookStatus !== HOOK_STATUS.READY;

        const base = buildResponseData({
          hookStatus,
          framework: 'react',
          componentName,
          capabilities: makeCapabilities({ canRead, canWrite, canWriteHooks: false }),
          needsRefresh,
        });

        if (!componentFiber) {
          return Transport.createResponse(
            req.requestId,
            false,
            base,
            'React component fiber not found',
          );
        }

        if (hookStatus !== HOOK_STATUS.READY) {
          return Transport.createResponse(
            req.requestId,
            false,
            base,
            'React DevTools editing API unavailable. Use a Development build and refresh the page.',
          );
        }

        // Check current value for editability and record original
        const props = componentFiber.memoizedProps;
        const read = getValueAtPath(props, path);
        if (read.ok && read.existed && !Serializer.isEditablePrimitive(read.value)) {
          return Transport.createResponse(
            req.requestId,
            false,
            base,
            'Target prop is not a primitive (read-only)',
          );
        }

        // Try renderers with overrideProps
        const candidates = (hookInfo.editableRenderers || [])
          .map((r) => r.renderer)
          .filter(Boolean);
        const preferred =
          fiberInfo.renderer && typeof fiberInfo.renderer.overrideProps === 'function'
            ? fiberInfo.renderer
            : null;
        const ordered = preferred
          ? [preferred, ...candidates.filter((r) => r !== preferred)]
          : candidates;

        let usedRenderer = null;
        let lastErr = null;

        for (const renderer of ordered) {
          try {
            renderer.overrideProps(componentFiber, path, value);
            usedRenderer = renderer;
            break;
          } catch (err) {
            lastErr = err;
          }
        }

        if (!usedRenderer) {
          base.meta = { write: { method: 'overrideProps', error: safeString(lastErr) } };
          return Transport.createResponse(
            req.requestId,
            false,
            base,
            'Failed to write props via overrideProps',
          );
        }

        ReactAdapter.recordOriginal(componentFiber, usedRenderer, path, read.existed, read.value);
        base.meta = { write: { method: 'overrideProps' } };

        return Transport.createResponse(req.requestId, true, base);
      }

      return Transport.createResponse(req.requestId, false, undefined, 'Not a React component');
    },

    /**
     * Handle 'reset' operation - Restore original props values
     */
    handleReset(req) {
      const target = this.resolveTarget(req.locator);
      if (!target) {
        return Transport.createResponse(
          req.requestId,
          false,
          undefined,
          'Target element not found',
        );
      }

      const preStatus = ReactAdapter.detectStatus();
      if (preStatus.hookStatus === HOOK_STATUS.HOOK_MISSING) {
        ReactAdapter.installMinimalHook();
      }

      const hookInfo = ReactAdapter.detectStatus();
      const hookStatus =
        preStatus.hookStatus === HOOK_STATUS.HOOK_MISSING
          ? HOOK_STATUS.HOOK_MISSING
          : hookInfo.hookStatus;

      const fw = FrameworkDetector.detect(target);

      if (fw.framework === 'react') {
        const fiberInfo = ReactAdapter.resolveFiberWithRenderer(target, hookInfo);
        const componentFiber = fiberInfo.fiber
          ? ReactAdapter.findNearestComponentFiber(fiberInfo.fiber)
          : null;

        const componentName = componentFiber
          ? ReactAdapter.getComponentName(componentFiber)
          : undefined;
        const canRead = Boolean(componentFiber);
        const canWrite = hookStatus === HOOK_STATUS.READY && Boolean(componentFiber);
        const needsRefresh = canRead && hookStatus !== HOOK_STATUS.READY;

        const base = buildResponseData({
          hookStatus,
          framework: 'react',
          componentName,
          capabilities: makeCapabilities({ canRead, canWrite, canWriteHooks: false }),
          needsRefresh,
        });

        if (!componentFiber) {
          return Transport.createResponse(
            req.requestId,
            false,
            base,
            'React component fiber not found',
          );
        }

        const store = ReactAdapter.getOriginals(componentFiber);
        if (!store?.originals?.size) {
          base.meta = { reset: { method: 'refresh', reason: 'noOverrides' } };
          base.needsRefresh = true;
          return Transport.createResponse(req.requestId, true, base);
        }

        if (hookStatus !== HOOK_STATUS.READY) {
          base.meta = { reset: { method: 'refresh', reason: 'hookNotReady' } };
          base.needsRefresh = true;
          return Transport.createResponse(req.requestId, true, base);
        }

        const renderer = store.renderer;
        if (!renderer || typeof renderer.overrideProps !== 'function') {
          base.meta = { reset: { method: 'refresh', reason: 'missingRenderer' } };
          base.needsRefresh = true;
          return Transport.createResponse(req.requestId, true, base);
        }

        let reverted = 0;
        for (const entry of store.originals.values()) {
          try {
            renderer.overrideProps(componentFiber, entry.path, entry.value);
            reverted++;
          } catch {
            // Continue reverting others
          }
        }

        ReactAdapter.clearOriginals(componentFiber);
        base.meta = { reset: { method: 'overrideProps', reverted } };

        return Transport.createResponse(req.requestId, true, base);
      }

      return Transport.createResponse(req.requestId, false, undefined, 'Not a React component');
    },

    /**
     * Handle 'cleanup' operation - Dispose agent
     */
    handleCleanup(req) {
      const resp = Transport.createResponse(req.requestId, true, {
        meta: { cleanup: { ok: true } },
      });
      Lifecycle.dispose('request');
      return resp;
    },

    /**
     * Route request to appropriate handler
     */
    handle(req) {
      switch (req.op) {
        case 'probe':
          return this.handleProbe(req);
        case 'read':
          return this.handleRead(req);
        case 'write':
          return this.handleWrite(req);
        case 'reset':
          return this.handleReset(req);
        case 'cleanup':
          return this.handleCleanup(req);
        default:
          return Transport.createResponse(
            req.requestId,
            false,
            undefined,
            `Unsupported op: ${safeString(req.op)}`,
          );
      }
    },
  };

  // =============================================================================
  // Lifecycle Management
  // =============================================================================

  const Lifecycle = {
    disposed: false,

    onRequestEvent(event) {
      try {
        if (Lifecycle.disposed) return;

        const detail = event?.detail;
        const req = Transport.normalizeRequest(detail);
        if (!req) return;

        const resp = Handlers.handle(req);
        Transport.dispatchResponse(resp);
      } catch (err) {
        try {
          const requestId = event?.detail?.requestId;
          if (typeof requestId === 'string' && requestId) {
            Transport.dispatchResponse(
              Transport.createResponse(requestId, false, undefined, safeString(err)),
            );
          }
        } catch {
          // ignore
        }
      }
    },

    onCleanupEvent() {
      Lifecycle.dispose('external-event');
    },

    dispose(reason) {
      if (this.disposed) return;
      this.disposed = true;

      try {
        window.removeEventListener(EVENT_NAME.REQUEST, this.onRequestEvent, true);
        window.removeEventListener(EVENT_NAME.CLEANUP, this.onCleanupEvent, true);
      } catch {
        // ignore
      }

      try {
        delete window[GLOBAL_KEY];
      } catch {
        // ignore
      }

      if (reason) {
        logWarn('Disposed:', reason);
      }
    },

    init() {
      // Use capture phase to avoid page stopPropagation interfering
      window.addEventListener(EVENT_NAME.REQUEST, this.onRequestEvent, true);
      window.addEventListener(EVENT_NAME.CLEANUP, this.onCleanupEvent, true);

      window[GLOBAL_KEY] = {
        version: PROTOCOL_VERSION,
        dispose: () => this.dispose('manual'),
      };

      // Early injection: install minimal hook before React loads (document_start)
      // This is critical for capturing React renderers that initialize early
      if (document.readyState === 'loading') {
        try {
          const status = ReactAdapter.detectStatus();
          if (status.hookStatus === HOOK_STATUS.HOOK_MISSING) {
            ReactAdapter.installMinimalHook();
            logWarn('Installed minimal hook during early injection');
          }
        } catch (err) {
          // Best-effort: early injection may fail in some environments
          logWarn('Early hook injection failed:', err);
        }
      }
    },
  };

  // Initialize
  Lifecycle.init();
})();
