/**
 * GooseFactory Modal Utilities
 * Shared helpers for all HITL modals — context reading, postMessage, timing, metrics.
 * 
 * Usage: Include inline in each modal HTML file via copy, or reference for consistency.
 * Each modal is self-contained, so this serves as the canonical source.
 */

const ModalUtils = (() => {
  'use strict';

  // ═══════════════════════════════════════
  // CONTEXT
  // ═══════════════════════════════════════

  /**
   * Read the injected factory context.
   * Falls back to URL params and defaults for dev/preview mode.
   */
  function getContext() {
    const ctx = window.__FACTORY_CONTEXT__ || {};
    return {
      modalType:          ctx.modalType          || getParam('modal_type') || 'unknown',
      modalVersion:       ctx.modalVersion       || '1.0.0',
      sessionId:          ctx.sessionId          || generateId(),
      pipelineId:         ctx.pipelineId         || getParam('pipeline_id') || 'demo_pipe',
      pipelineName:       ctx.pipelineName       || 'Demo Pipeline',
      itemId:             ctx.itemId             || getParam('item_id') || 'demo_item',
      itemName:           ctx.itemName           || 'Demo Item',
      deliverablePreview: ctx.deliverablePreview || '',
      deliverableContent: ctx.deliverableContent || '',
      contentBefore:      ctx.contentBefore      || '',
      contentAfter:       ctx.contentAfter       || '',
      testResults:        ctx.testResults        || null,
      diffSummary:        ctx.diffSummary        || null,
      metrics:            ctx.metrics            || {},
      items:              ctx.items              || [],
      dimensions:         ctx.dimensions         || [],
      checklistItems:     ctx.checklistItems     || [],
      timerSeconds:       ctx.timerSeconds       || 60,
      theme:              ctx.theme              || 'dark',
      accentColor:        ctx.accentColor        || '#00AAFF',
    };
  }

  function getParam(name) {
    try {
      return new URLSearchParams(window.location.search).get(name);
    } catch { return null; }
  }

  function generateId() {
    return 'sess_' + Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
  }

  // ═══════════════════════════════════════
  // POST MESSAGE HELPERS
  // ═══════════════════════════════════════

  function postToHost(message) {
    try {
      window.parent.postMessage(message, '*');
    } catch (e) {
      console.warn('[ModalUtils] postMessage failed:', e);
    }
  }

  function signalReady(modalType, version) {
    postToHost({
      type: 'factory_modal_ready',
      modalType: modalType || 'unknown',
      version: version || '1.0.0',
    });
  }

  function signalClose(reason) {
    postToHost({
      type: 'factory_modal_close',
      reason: reason || 'cancelled',
    });
  }

  function signalResize(height, width) {
    const msg = { type: 'factory_modal_resize', height };
    if (width) msg.width = width;
    postToHost(msg);
  }

  function signalError(error, details) {
    postToHost({
      type: 'factory_modal_error',
      error: String(error),
      details: details || null,
    });
  }

  /**
   * Submit the modal response — the main data payload.
   * @param {object} opts
   * @param {string} opts.modalType
   * @param {string} opts.modalVersion
   * @param {object} opts.ctx        - The context object from getContext()
   * @param {object} opts.feedback   - FeedbackPayload
   * @param {object} opts.metrics    - Extra ModalMetrics (merged with auto-collected)
   * @param {number} opts.startTime  - Performance.now() or Date.now() when modal opened
   */
  function submitResponse(opts) {
    const now = Date.now();
    const ctx = opts.ctx || getContext();
    const autoMetrics = collectAutoMetrics(opts.startTime);

    postToHost({
      type: 'factory_modal_response',
      modalType:    opts.modalType    || ctx.modalType,
      modalVersion: opts.modalVersion || ctx.modalVersion || '1.0.0',
      pipelineId:   ctx.pipelineId,
      itemId:       ctx.itemId,
      sessionId:    ctx.sessionId,
      timestamp:    new Date(now).toISOString(),
      responseTimeMs: now - (opts.startTime || now),
      feedback:     opts.feedback || {},
      meta:         Object.assign({}, autoMetrics, opts.metrics || {}),
    });
  }

  // ═══════════════════════════════════════
  // TIMING & METRICS
  // ═══════════════════════════════════════

  let _interactions = 0;
  let _firstInteractionTime = null;
  let _fieldsModified = new Set();
  let _modalOpenTime = Date.now();

  /** Call once on DOMContentLoaded to start auto-tracking */
  function initMetrics() {
    _modalOpenTime = Date.now();
    _interactions = 0;
    _firstInteractionTime = null;
    _fieldsModified = new Set();

    document.addEventListener('click',   _trackInteraction, true);
    document.addEventListener('touchend', _trackInteraction, true);
    document.addEventListener('input',   _trackInput, true);
  }

  function _trackInteraction() {
    _interactions++;
    if (!_firstInteractionTime) _firstInteractionTime = Date.now();
  }

  function _trackInput(e) {
    _trackInteraction();
    if (e.target && (e.target.name || e.target.id || e.target.dataset.field)) {
      _fieldsModified.add(e.target.name || e.target.id || e.target.dataset.field);
    }
  }

  function trackFieldModified(fieldName) {
    _fieldsModified.add(fieldName);
  }

  function collectAutoMetrics(startTime) {
    const now = Date.now();
    const start = startTime || _modalOpenTime;
    return {
      timeToFirstInteractionMs: _firstInteractionTime ? _firstInteractionTime - start : null,
      timeToDecisionMs:         now - start,
      fieldsModified:           Array.from(_fieldsModified),
      totalInteractions:        _interactions,
      revisits:                 0,
      viewportSize: {
        width:  window.innerWidth,
        height: window.innerHeight,
      },
      deviceType: window.innerWidth < 768 ? 'mobile' : 'desktop',
    };
  }

  // ═══════════════════════════════════════
  // ANIMATION HELPERS
  // ═══════════════════════════════════════

  /** Apply a CSS class temporarily for animation, then remove it */
  function flashClass(element, className, durationMs) {
    if (!element) return;
    element.classList.add(className);
    setTimeout(() => element.classList.remove(className), durationMs || 500);
  }

  /** Simple eased lerp for smooth value transitions */
  function animateValue(from, to, durationMs, onUpdate, onDone) {
    const start = performance.now();
    function tick(now) {
      const elapsed = now - start;
      const t = Math.min(elapsed / durationMs, 1);
      const eased = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; // easeInOut
      onUpdate(from + (to - from) * eased);
      if (t < 1) requestAnimationFrame(tick);
      else if (onDone) onDone();
    }
    requestAnimationFrame(tick);
  }

  // ═══════════════════════════════════════
  // PUBLIC API
  // ═══════════════════════════════════════

  return {
    getContext,
    postToHost,
    signalReady,
    signalClose,
    signalResize,
    signalError,
    submitResponse,
    initMetrics,
    trackFieldModified,
    collectAutoMetrics,
    flashClass,
    animateValue,
    generateId,
  };
})();
