// ==========================================
// NOTIFICATIONS COMPONENT
// ==========================================
// Real-time silent refresh (no notification bubbles)

/**
 * Format JSON object for display in notifications
 * Parses JSON strings and formats objects into readable key-value pairs
 * @param {Object|string} obj - Object or JSON string to format
 * @param {number} maxLen - Max string length (unused, kept for compatibility)
 * @returns {string} Formatted string with key: value pairs
 */
function formatJsonDetails(obj, maxLen = 150) {
  // Handle null/undefined
  if (obj === null || obj === undefined) return '';

  // If it is a string, try to parse as JSON
  if (typeof obj === 'string') {
    // Check if it looks like JSON
    const trimmed = obj.trim();
    if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
        (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
      try {
        obj = JSON.parse(trimmed);
      } catch (e) {
        // Not valid JSON, return as-is
        return obj;
      }
    } else {
      // Plain string, return as-is
      return obj;
    }
  }

  // Handle non-objects (numbers, booleans, etc.)
  if (typeof obj !== 'object') return String(obj);

  // Handle arrays
  if (Array.isArray(obj)) {
    if (obj.length === 0) return '(empty array)';
    return obj.slice(0, 5).map((item, i) => {
      const itemStr = typeof item === 'object' ? JSON.stringify(item) : String(item);
      return `[${i}] ${itemStr.length > 50 ? itemStr.substring(0, 47) + '...' : itemStr}`;
    }).join('\n') + (obj.length > 5 ? `\n... +${obj.length - 5} more` : '');
  }

  // Handle objects - format as readable key: value pairs
  try {
    const entries = Object.entries(obj);
    if (entries.length === 0) return '(empty object)';

    // Format each entry with proper value display
    const lines = entries.slice(0, 8).map(([key, val]) => {
      let valStr;
      if (val === null) {
        valStr = 'null';
      } else if (val === undefined) {
        valStr = 'undefined';
      } else if (typeof val === 'boolean') {
        valStr = val ? 'true' : 'false';
      } else if (typeof val === 'number') {
        valStr = String(val);
      } else if (typeof val === 'object') {
        valStr = JSON.stringify(val);
        if (valStr.length > 40) valStr = valStr.substring(0, 37) + '...';
      } else {
        valStr = String(val);
        if (valStr.length > 50) valStr = valStr.substring(0, 47) + '...';
      }
      return `${key}: ${valStr}`;
    });

    if (entries.length > 8) {
      lines.push(`... +${entries.length - 8} more fields`);
    }

    return lines.join('\n');
  } catch (e) {
    // Fallback to stringified version
    const str = JSON.stringify(obj);
    return str.length > 200 ? str.substring(0, 197) + '...' : str;
  }
}

let wsConnection = null;
let autoRefreshInterval = null;
let lastDataHash = null;
const AUTO_REFRESH_INTERVAL_MS = 30000; // 30 seconds

// Custom event handlers registry for components to subscribe to specific events
const wsEventHandlers = {};

/**
 * Register a custom handler for a specific WebSocket event type
 * @param {string} eventType - The event type to listen for
 * @param {Function} handler - The handler function
 */
function registerWsEventHandler(eventType, handler) {
  if (!wsEventHandlers[eventType]) {
    wsEventHandlers[eventType] = [];
  }
  wsEventHandlers[eventType].push(handler);
}

/**
 * Unregister a custom handler for a specific WebSocket event type
 * @param {string} eventType - The event type
 * @param {Function} handler - The handler function to remove
 */
function unregisterWsEventHandler(eventType, handler) {
  if (wsEventHandlers[eventType]) {
    wsEventHandlers[eventType] = wsEventHandlers[eventType].filter(h => h !== handler);
  }
}

/**
 * Dispatch event to registered handlers
 * @param {string} eventType - The event type
 * @param {Object} data - The full event data
 */
function dispatchToEventHandlers(eventType, data) {
  if (wsEventHandlers[eventType]) {
    wsEventHandlers[eventType].forEach(handler => {
      try {
        handler(data);
      } catch (e) {
        console.error('[WS] Error in custom handler for', eventType, e);
      }
    });
  }
}

// ========== WebSocket Connection ==========
function initWebSocket() {
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  const wsUrl = `${protocol}//${window.location.host}/ws`;

  try {
    wsConnection = new WebSocket(wsUrl);

    wsConnection.onopen = () => {
      console.log('[WS] Connected');
    };

    wsConnection.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        handleNotification(data);
      } catch (e) {
        console.error('[WS] Failed to parse message:', e);
      }
    };

    wsConnection.onclose = () => {
      console.log('[WS] Disconnected, reconnecting in 5s...');
      setTimeout(initWebSocket, 5000);
    };

    wsConnection.onerror = (error) => {
      console.error('[WS] Error:', error);
    };
  } catch (e) {
    console.log('[WS] WebSocket not available, using polling');
  }
}

// ========== Notification Handler ==========
function handleNotification(data) {
  const { type, payload } = data;

  // Silent refresh - no notification bubbles
  switch (type) {
    case 'session_updated':
    case 'summary_written':
    case 'task_completed':
    case 'new_session':
      // Just refresh data silently
      refreshIfNeeded();
      // Optionally highlight in carousel if it's the current session
      if (payload.sessionId && typeof carouselGoTo === 'function') {
        carouselGoTo(payload.sessionId);
      }
      break;

    case 'SESSION_CREATED':
    case 'SESSION_ARCHIVED':
    case 'TASK_UPDATED':
    case 'SESSION_UPDATED':
    case 'TASK_CREATED':
    case 'SUMMARY_WRITTEN':
    case 'PLAN_UPDATED':
    case 'REVIEW_UPDATED':
    case 'CONTENT_WRITTEN':
    case 'FILE_DELETED':
    case 'DIRECTORY_CREATED':
      // Route to state reducer for granular updates
      if (typeof handleWorkflowEvent === 'function') {
        handleWorkflowEvent({ type, ...payload });
      } else {
        // Fallback to full refresh if reducer not available
        refreshIfNeeded();
      }
      break;

    case 'tool_execution':
      // Handle tool execution notifications from MCP tools
      handleToolExecutionNotification(payload);
      break;

    case 'cli_execution':
      // Handle CLI command notifications (ccw cli -p)
      handleCliCommandNotification(payload);
      break;

    // CLI Tool Execution Events
    case 'CLI_EXECUTION_STARTED':
      if (typeof handleCliExecutionStarted === 'function') {
        handleCliExecutionStarted(payload);
      }
      break;

    case 'CLI_OUTPUT':
      if (typeof handleCliOutput === 'function') {
        handleCliOutput(payload);
      }
      break;

    case 'CLI_EXECUTION_COMPLETED':
      if (typeof handleCliExecutionCompleted === 'function') {
        handleCliExecutionCompleted(payload);
      }
      break;

    case 'CLI_EXECUTION_ERROR':
      if (typeof handleCliExecutionError === 'function') {
        handleCliExecutionError(payload);
      }
      break;

    // CLI Review Events
    case 'CLI_REVIEW_UPDATED':
      if (typeof handleCliReviewUpdated === 'function') {
        handleCliReviewUpdated(payload);
      }
      // Also refresh CLI history to show review status
      if (typeof refreshCliHistory === 'function') {
        refreshCliHistory();
      }
      break;

    // System Notify Events (from CLI commands)
    case 'REFRESH_REQUIRED':
      handleRefreshRequired(payload);
      break;

    case 'MEMORY_UPDATED':
      if (typeof handleMemoryUpdated === 'function') {
        handleMemoryUpdated(payload);
      }
      // Force refresh of memory view
      if (typeof loadMemoryStats === 'function') {
        loadMemoryStats().then(function() {
          if (typeof renderHotspotsColumn === 'function') renderHotspotsColumn();
        }).catch(function(err) {
          console.error('[Memory] Failed to refresh stats:', err);
        });
      }
      break;

    case 'HISTORY_UPDATED':
      // Refresh CLI history when updated externally
      if (typeof refreshCliHistory === 'function') {
        refreshCliHistory();
      }
      break;

    case 'INSIGHT_GENERATED':
      // Refresh insights when new insight is generated
      if (typeof loadInsightsHistory === 'function') {
        loadInsightsHistory();
      }
      break;

    case 'ACTIVE_MEMORY_SYNCED':
      // Handle Active Memory sync completion
      if (typeof addGlobalNotification === 'function') {
        const { filesAnalyzed, tool, usedCli } = payload;
        const method = usedCli ? `CLI (${tool})` : 'Basic';
        addGlobalNotification(
          'success',
          'Active Memory synced',
          {
            'Files Analyzed': filesAnalyzed,
            'Method': method,
            'Timestamp': new Date(payload.timestamp).toLocaleTimeString()
          },
          'Memory'
        );
      }
      // Refresh Active Memory status
      if (typeof loadActiveMemoryStatus === 'function') {
        loadActiveMemoryStatus().catch(function(err) {
          console.error('[Active Memory] Failed to refresh status:', err);
        });
      }
      console.log('[Active Memory] Sync completed:', payload);
      break;

    case 'CLAUDE_FILE_SYNCED':
      // Handle CLAUDE.md file sync completion
      if (typeof addGlobalNotification === 'function') {
        const { path, level, tool, mode } = payload;
        const fileName = path.split(/[/\\]/).pop();
        addGlobalNotification(
          'success',
          `${fileName} synced`,
          {
            'Level': level,
            'Tool': tool,
            'Mode': mode,
            'Time': new Date(payload.timestamp).toLocaleTimeString()
          },
          'CLAUDE.md'
        );
      }
      // Refresh file list
      if (typeof loadClaudeFiles === 'function') {
        loadClaudeFiles().then(() => {
          // Re-render the view to show updated content
          if (typeof renderClaudeManager === 'function') {
            renderClaudeManager();
          }
        }).catch(err => console.error('[CLAUDE.md] Failed to refresh files:', err));
      }
      console.log('[CLAUDE.md] Sync completed:', payload);
      break;

    case 'CLI_TOOL_INSTALLED':
      // Handle CLI tool installation completion
      if (typeof addGlobalNotification === 'function') {
        const { tool } = payload;
        addGlobalNotification(
          'success',
          `${tool} installed successfully`,
          {
            'Tool': tool,
            'Time': new Date(payload.timestamp).toLocaleTimeString()
          },
          'CLI Tools'
        );
      }
      // Refresh CLI manager
      if (typeof loadCliToolStatus === 'function') {
        loadCliToolStatus().then(() => {
          if (typeof renderToolsSection === 'function') {
            renderToolsSection();
          }
        }).catch(err => console.error('[CLI Tools] Failed to refresh status:', err));
      }
      console.log('[CLI Tools] Installation completed:', payload);
      break;

    case 'CLI_TOOL_UNINSTALLED':
      // Handle CLI tool uninstallation completion
      if (typeof addGlobalNotification === 'function') {
        const { tool } = payload;
        addGlobalNotification(
          'success',
          `${tool} uninstalled successfully`,
          {
            'Tool': tool,
            'Time': new Date(payload.timestamp).toLocaleTimeString()
          },
          'CLI Tools'
        );
      }
      // Refresh CLI manager
      if (typeof loadCliToolStatus === 'function') {
        loadCliToolStatus().then(() => {
          if (typeof renderToolsSection === 'function') {
            renderToolsSection();
          }
        }).catch(err => console.error('[CLI Tools] Failed to refresh status:', err));
      }
      console.log('[CLI Tools] Uninstallation completed:', payload);
      break;

    case 'CODEXLENS_INSTALLED':
      // Handle CodexLens installation completion
      if (typeof addGlobalNotification === 'function') {
        const { version } = payload;
        addGlobalNotification(
          'success',
          `CodexLens installed successfully`,
          {
            'Version': version || 'latest',
            'Time': new Date(payload.timestamp).toLocaleTimeString()
          },
          'CodexLens'
        );
      }
      // Refresh CLI status if active
      if (typeof loadCodexLensStatus === 'function') {
        loadCodexLensStatus().then(() => {
          if (typeof renderCliStatus === 'function') {
            renderCliStatus();
          }
        });
      }
      console.log('[CodexLens] Installation completed:', payload);
      break;

    case 'CODEXLENS_UNINSTALLED':
      // Handle CodexLens uninstallation completion
      if (typeof addGlobalNotification === 'function') {
        addGlobalNotification(
          'success',
          `CodexLens uninstalled successfully`,
          {
            'Time': new Date(payload.timestamp).toLocaleTimeString()
          },
          'CodexLens'
        );
      }
      // Refresh CLI status if active
      if (typeof loadCodexLensStatus === 'function') {
        loadCodexLensStatus().then(() => {
          if (typeof renderCliStatus === 'function') {
            renderCliStatus();
          }
        });
      }
      console.log('[CodexLens] Uninstallation completed:', payload);
      break;

    case 'CODEXLENS_INDEX_PROGRESS':
      // Handle CodexLens index progress updates
      dispatchToEventHandlers('CODEXLENS_INDEX_PROGRESS', data);
      console.log('[CodexLens] Index progress:', payload.stage, payload.percent + '%');
      break;

    default:
      console.log('[WS] Unknown notification type:', type);
  }
}

/**
 * Handle tool execution notifications from MCP tools
 * @param {Object} payload - Tool execution payload
 */
function handleToolExecutionNotification(payload) {
  const { toolName, status, params, result, error, timestamp } = payload;

  // Determine notification type and message
  let notifType = 'info';
  let message = `Tool: ${toolName}`;
  let details = null;

  switch (status) {
    case 'started':
      notifType = 'info';
      message = `Executing ${toolName}...`;
      // Pass raw object for HTML formatting
      if (params) {
        details = params;
      }
      break;

    case 'completed':
      notifType = 'success';
      message = `${toolName} completed`;
      // Pass raw object for HTML formatting
      if (result) {
        if (result._truncated) {
          details = result.preview;
        } else {
          details = result;
        }
      }
      break;

    case 'failed':
      notifType = 'error';
      message = `${toolName} failed`;
      details = error || 'Unknown error';
      break;

    default:
      notifType = 'info';
      message = `${toolName}: ${status}`;
  }

  // Add to global notifications - pass objects directly for HTML formatting
  if (typeof addGlobalNotification === 'function') {
    addGlobalNotification(notifType, message, details, 'MCP');
  }

  // Log to console
  console.log(`[MCP] ${status}: ${toolName}`, payload);
}

/**
 * Handle CLI command notifications (ccw cli -p)
 * @param {Object} payload - CLI execution payload
 */
function handleCliCommandNotification(payload) {
  const { event, tool, mode, prompt_preview, execution_id, success, duration_ms, status, error, turn_count, custom_id } = payload;

  let notifType = 'info';
  let message = '';
  let details = null;

  switch (event) {
    case 'started':
      notifType = 'info';
      message = `CLI ${tool} started`;
      // Pass structured object for rich display
      details = {
        mode: mode,
        prompt: prompt_preview
      };
      if (custom_id) {
        details.id = custom_id;
      }
      break;

    case 'completed':
      if (success) {
        notifType = 'success';
        const turnStr = turn_count > 1 ? ` (turn ${turn_count})` : '';
        message = `CLI ${tool} completed${turnStr}`;
        // Pass structured object for rich display
        details = {
          duration: duration_ms ? `${(duration_ms / 1000).toFixed(1)}s` : '-',
          execution_id: execution_id
        };
        if (turn_count > 1) {
          details.turns = turn_count;
        }
      } else {
        notifType = 'error';
        message = `CLI ${tool} failed`;
        details = {
          status: status || 'Unknown error',
          execution_id: execution_id
        };
      }
      break;

    case 'error':
      notifType = 'error';
      message = `CLI ${tool} error`;
      details = error || 'Unknown error';
      break;

    default:
      notifType = 'info';
      message = `CLI ${tool}: ${event}`;
  }

  // Add to global notifications - pass objects for HTML formatting
  if (typeof addGlobalNotification === 'function') {
    addGlobalNotification(notifType, message, details, 'CLI');
  }

  // Refresh CLI history if on history view
  if (event === 'completed' && typeof currentView !== 'undefined' && 
      (currentView === 'history' || currentView === 'cli-history')) {
    if (typeof loadCliHistory === 'function' && typeof renderCliHistoryView === 'function') {
      loadCliHistory().then(() => renderCliHistoryView());
    }
  }

  // Log to console
  console.log(`[CLI Command] ${event}: ${tool}`, payload);
}

// ========== Auto Refresh ==========
function initAutoRefresh() {
  // Calculate initial hash
  lastDataHash = calculateDataHash();

  // Start polling interval
  autoRefreshInterval = setInterval(checkForChanges, AUTO_REFRESH_INTERVAL_MS);
}

function calculateDataHash() {
  if (!workflowData) return null;

  // Simple hash based on key data points
  const hashData = {
    activeSessions: (workflowData.activeSessions || []).length,
    archivedSessions: (workflowData.archivedSessions || []).length,
    totalTasks: workflowData.statistics?.totalTasks || 0,
    completedTasks: workflowData.statistics?.completedTasks || 0,
    generatedAt: workflowData.generatedAt
  };

  return JSON.stringify(hashData);
}

async function checkForChanges() {
  if (!window.SERVER_MODE) return;

  try {
    const response = await fetch(`/api/data?path=${encodeURIComponent(projectPath)}`);
    if (!response.ok) return;

    const newData = await response.json();
    const newHash = JSON.stringify({
      activeSessions: (newData.activeSessions || []).length,
      archivedSessions: (newData.archivedSessions || []).length,
      totalTasks: newData.statistics?.totalTasks || 0,
      completedTasks: newData.statistics?.completedTasks || 0,
      generatedAt: newData.generatedAt
    });

    if (newHash !== lastDataHash) {
      lastDataHash = newHash;
      // Silent refresh - no notification
      await refreshWorkspaceData(newData);
    }
  } catch (e) {
    console.error('[AutoRefresh] Check failed:', e);
  }
}

async function refreshIfNeeded() {
  if (!window.SERVER_MODE) return;

  try {
    const response = await fetch(`/api/data?path=${encodeURIComponent(projectPath)}`);
    if (!response.ok) return;

    const newData = await response.json();
    await refreshWorkspaceData(newData);
  } catch (e) {
    console.error('[Refresh] Failed:', e);
  }
}

async function refreshWorkspaceData(newData) {
  // Update global data
  window.workflowData = newData;

  // Clear and repopulate stores
  Object.keys(sessionDataStore).forEach(k => delete sessionDataStore[k]);
  Object.keys(liteTaskDataStore).forEach(k => delete liteTaskDataStore[k]);

  [...(newData.activeSessions || []), ...(newData.archivedSessions || [])].forEach(s => {
    const key = `session-${s.session_id}`.replace(/[^a-zA-Z0-9-]/g, '-');
    sessionDataStore[key] = s;
  });

  [...(newData.liteTasks?.litePlan || []), ...(newData.liteTasks?.liteFix || [])].forEach(s => {
    const key = `lite-${s.session_id}`.replace(/[^a-zA-Z0-9-]/g, '-');
    liteTaskDataStore[key] = s;
  });

  // Update UI silently
  updateStats();
  updateBadges();
  updateCarousel();

  // Re-render current view if needed
  if (currentView === 'sessions') {
    renderSessions();
  } else if (currentView === 'liteTasks') {
    renderLiteTasks();
  }

  lastDataHash = calculateDataHash();
}

/**
 * Handle REFRESH_REQUIRED events from CLI commands
 * @param {Object} payload - Contains scope (memory|history|insights|all)
 */
function handleRefreshRequired(payload) {
  const scope = payload?.scope || 'all';
  console.log('[WS] Refresh required for scope:', scope);

  switch (scope) {
    case 'memory':
      // Refresh memory stats and graph
      if (typeof loadMemoryStats === 'function') {
        loadMemoryStats().then(function() {
          if (typeof renderHotspotsColumn === 'function') renderHotspotsColumn();
        });
      }
      if (typeof loadMemoryGraph === 'function') {
        loadMemoryGraph();
      }
      break;

    case 'history':
      // Refresh CLI history
      if (typeof refreshCliHistory === 'function') {
        refreshCliHistory();
      }
      break;

    case 'insights':
      // Refresh insights history
      if (typeof loadInsightsHistory === 'function') {
        loadInsightsHistory();
      }
      break;

    case 'all':
    default:
      // Refresh everything
      refreshIfNeeded();
      if (typeof loadMemoryStats === 'function') {
        loadMemoryStats().then(function() {
          if (typeof renderHotspotsColumn === 'function') renderHotspotsColumn();
        });
      }
      if (typeof refreshCliHistory === 'function') {
        refreshCliHistory();
      }
      if (typeof loadInsightsHistory === 'function') {
        loadInsightsHistory();
      }
      break;
  }
}

// ========== Cleanup ==========
function stopAutoRefresh() {
  if (autoRefreshInterval) {
    clearInterval(autoRefreshInterval);
    autoRefreshInterval = null;
  }
}

function closeWebSocket() {
  if (wsConnection) {
    wsConnection.close();
    wsConnection = null;
  }
}

// ========== Navigation Helper ==========
function goToSession(sessionId) {
  // Find session in carousel and navigate
  const sessionKey = `session-${sessionId}`.replace(/[^a-zA-Z0-9-]/g, '-');

  // Jump to session in carousel if visible
  if (typeof carouselGoTo === 'function') {
    carouselGoTo(sessionId);
  }

  // Navigate to session detail
  if (sessionDataStore[sessionKey]) {
    showSessionDetailPage(sessionKey);
  }
}
