/* eslint-disable @typescript-eslint/no-unused-vars */
/**
 * Console Log Collector for TRRO Demo
 * Intercepts console logs, stores them in IndexedDB, and provides export functionality
 */

(function () {
  'use strict';

  // ==================== Configuration ====================
  const DB_NAME = 'TRRODemoLogs';
  const DB_VERSION = 1;
  const STORE_NAME = 'logs';
  const MAX_LOGS_PER_SESSION = 50000; // Maximum logs per session before auto-cleanup
  const MAX_LOG_SIZE = 10 * 1024; // Maximum single log size: 10KB (prevent huge objects)

  // ==================== Session Management ====================
  /**
   * Generate unique session ID based on random value + timestamp
   * @returns {string} Session ID
   */
  function generateSessionId() {
    const randomPart = Math.random().toString(36).substring(2, 15);
    const timestampPart = Date.now().toString(36);
    return `${randomPart}-${timestampPart}`;
  }

  // Get or create session ID
  const SESSION_ID = generateSessionId();
  let logSequence = 0; // Log sequence number for ordering

  // ==================== IndexedDB Management ====================
  let db = null;

  /**
   * Initialize IndexedDB
   */
  function initDB() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(DB_NAME, DB_VERSION);

      request.onerror = () => {
        console.error('[LogCollector] Failed to open IndexedDB:', request.error);
        reject(request.error);
      };

      request.onsuccess = () => {
        db = request.result;
        resolve(db);
      };

      request.onupgradeneeded = (event) => {
        const db = event.target.result;

        // Create object store if not exists
        if (!db.objectStoreNames.contains(STORE_NAME)) {
          const objectStore = db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true });
          objectStore.createIndex('sessionId', 'sessionId', { unique: false });
          objectStore.createIndex('timestamp', 'timestamp', { unique: false });
          objectStore.createIndex('sequence', 'sequence', { unique: false });
        }
      };
    });
  }

  /**
   * Store log entry to IndexedDB
   * @param {Object} logEntry - Log entry object
   */
  function storeLog(logEntry) {
    if (!db) {
      console.error('[LogCollector] Database not initialized');
      return;
    }

    try {
      // Check log content size to prevent storing huge objects
      const contentSize = new Blob([logEntry.content || '']).size;
      if (contentSize > MAX_LOG_SIZE) {
        // Truncate large logs
        logEntry.content = `${(logEntry.content || '').substring(0, MAX_LOG_SIZE)}... [TRUNCATED]`;
      }

      const transaction = db.transaction([STORE_NAME], 'readwrite');
      const objectStore = transaction.objectStore(STORE_NAME);
      objectStore.add(logEntry);

      // Silent error handling to avoid impacting performance
      transaction.onerror = () => {
        // Quota exceeded or other errors - silently skip
      };
    } catch (error) {
      // Silently fail to avoid impacting main application
      if (error.name === 'QuotaExceededError') {
        // Try to clear old logs
        clearSessionLogs().catch(() => {});
      }
    }
  }

  /**
   * Get all logs for current session
   */
  function getSessionLogs() {
    return new Promise((resolve, reject) => {
      if (!db) {
        reject(new Error('Database not initialized'));
        return;
      }

      try {
        const transaction = db.transaction([STORE_NAME], 'readonly');
        const objectStore = transaction.objectStore(STORE_NAME);
        const index = objectStore.index('sessionId');
        const request = index.getAll(SESSION_ID);

        request.onsuccess = () => {
          // Sort by sequence to maintain order
          const logs = request.result.sort((a, b) => a.sequence - b.sequence);
          resolve(logs);
        };

        request.onerror = () => {
          reject(request.error);
        };
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * Clear logs for current session
   */
  function clearSessionLogs() {
    return new Promise((resolve, reject) => {
      if (!db) {
        reject(new Error('Database not initialized'));
        return;
      }

      try {
        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const objectStore = transaction.objectStore(STORE_NAME);
        const index = objectStore.index('sessionId');
        const request = index.openCursor(IDBKeyRange.only(SESSION_ID));

        request.onsuccess = (event) => {
          const cursor = event.target.result;
          if (cursor) {
            cursor.delete();
            cursor.continue();
          } else {
            resolve();
          }
        };

        request.onerror = () => {
          reject(request.error);
        };
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * Synchronous-style cleanup (for page unload)
   * This initiates the cleanup without waiting for completion
   */
  function clearSessionLogsSync() {
    if (!db) {
      return;
    }

    try {
      const transaction = db.transaction([STORE_NAME], 'readwrite');
      const objectStore = transaction.objectStore(STORE_NAME);
      const index = objectStore.index('sessionId');
      const request = index.openCursor(IDBKeyRange.only(SESSION_ID));

      request.onsuccess = (event) => {
        const cursor = event.target.result;
        if (cursor) {
          cursor.delete();
          cursor.continue();
        }
      };

      // Don't handle errors - page is unloading
    } catch (error) {
      // Silently fail
    }
  }

  /**
   * Check log count and perform cleanup if needed
   */
  async function checkLogCount() {
    try {
      const logs = await getSessionLogs();
      if (logs.length > MAX_LOGS_PER_SESSION) {
        console.warn(`[LogCollector] Log count exceeded ${MAX_LOGS_PER_SESSION}, clearing old logs`);
        await clearSessionLogs();
      }
    } catch (error) {
      console.error('[LogCollector] Failed to check log count:', error);
    }
  }

  // ==================== SDK Log Parser ====================
  /**
   * Parse SDK log format
   * Format: [time][level][version][module][userId][roomId/remoteDeviceId][trackId] message content
   * Note: userId, roomId, trackId are all optional and may be empty strings
   *
   * Actual format examples:
   * - [2025-02-03 10:30:45.123][INFO][trro_web_sdk-2.4.5.8][MQTT] Message
   * - [2025-02-03 10:30:45.123][INFO][trro_web_sdk-2.4.5.8][Engine][user123][room456] Message
   * - [2025-02-03 10:30:45.123][INFO][trro_web_sdk-2.4.5.8][Renderer][user123][room456][track789] Message
   *
   * @param {string} message - Log message
   * @returns {Object|null} Parsed log object or null if not SDK format
   */
  function parseSDKLog(message) {
    // SDK log regex: match all [] fields after time, level, and version
    const sdkPattern =
      /^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\]\[([A-Z]+)\]\[trro_web_sdk-([\d.]+)\]((?:\[[^\]]*\])*)\s*(.*)$/;
    const match = message.match(sdkPattern);

    if (match) {
      const timestamp = match[1];
      const level = match[2];
      const version = match[3];
      const bracketFields = match[4]; // All remaining [] fields
      const messageContent = match[5];

      // Extract content from all [] fields
      const fields = [];
      const fieldPattern = /\[([^\]]*)\]/g;
      let fieldMatch;
      while ((fieldMatch = fieldPattern.exec(bracketFields)) !== null) {
        fields.push(fieldMatch[1]);
      }

      // Determine field mapping based on field count:
      // 1 field:  [module]
      // 2 fields: [module][userId]
      // 3 fields: [module][userId][roomId]
      // 4 fields: [module][userId][roomId][trackId]
      let module;
      let userId;
      let remoteDeviceId;
      let trackId;

      if (fields.length === 1) {
        [module] = fields;
      } else if (fields.length === 2) {
        [module, userId] = fields;
      } else if (fields.length === 3) {
        [module, userId, remoteDeviceId] = fields;
      } else if (fields.length >= 4) {
        [module, userId, remoteDeviceId, trackId] = fields;
      }

      return {
        isSDKLog: true,
        time: timestamp || null,
        level: level || 'LOG',
        sdkVersion: version || null,
        module: module || null,
        remoteDeviceId: userId || remoteDeviceId || null,
        fieldDeviceId: remoteDeviceId || null,
        trackId: trackId || null,
        content: messageContent?.trim() || '',
      };
    }

    return null;
  }

  /**
   * Format arguments to string
   */
  function formatArgs(args) {
    return Array.from(args)
      .map((arg) => {
        if (typeof arg === 'string') {
          return arg;
        }
        if (arg instanceof Error) {
          return `${arg.name}: ${arg.message}\n${arg.stack}`;
        }
        try {
          return JSON.stringify(arg, null, 2);
        } catch (e) {
          return String(arg);
        }
      })
      .join(' ');
  }

  // ==================== Console Hooking ====================
  const originalConsole = {
    log: console.log,
    error: console.error,
    warn: console.warn,
    info: console.info,
    debug: console.debug,
  };

  /**
   * Create console hook function
   */
  function createConsoleHook(level, originalFunc) {
    return function (...args) {
      // Call original console function
      originalFunc.apply(console, args);

      // Format message
      const message = formatArgs(args);

      // Try to parse as SDK log
      const sdkLog = parseSDKLog(message);

      const logEntry = {
        sessionId: SESSION_ID,
        sequence: logSequence,
        timestamp: Date.now(),
        level,
      };
      logSequence += 1;

      if (sdkLog) {
        // SDK log format
        Object.assign(logEntry, sdkLog);
      } else {
        // Regular log format
        logEntry.isSDKLog = false;
        logEntry.time = new Date().toISOString().replace('T', ' ').substring(0, 23);
        logEntry.content = message;
      }

      // Store to IndexedDB
      storeLog(logEntry);
    };
  }

  /**
   * Hook console methods
   */
  function hookConsole() {
    console.log = createConsoleHook('LOG', originalConsole.log);
    console.error = createConsoleHook('ERROR', originalConsole.error);
    console.warn = createConsoleHook('WARN', originalConsole.warn);
    console.info = createConsoleHook('INFO', originalConsole.info);
    console.debug = createConsoleHook('DEBUG', originalConsole.debug);
  }

  // ==================== Export Functionality ====================
  /**
   * Get translation text
   */
  function t(key, variables = {}) {
    // Use global t function if available, otherwise return key
    if (typeof window.t === 'function') {
      return window.t(key, variables);
    }
    return key;
  }

  /**
   * Export logs to file
   */
  async function exportLogs() {
    try {
      const logs = await getSessionLogs();

      if (logs.length === 0) {
        alert(t('log_collector_export_no_logs'));
        return;
      }

      // Format logs as text
      let logText = `TRRO Demo Logs Export\n`;
      logText += `Session ID: ${SESSION_ID}\n`;
      logText += `Export Time: ${new Date().toLocaleString()}\n`;
      logText += `Total Logs: ${logs.length}\n`;
      logText += `${'='.repeat(80)}\n\n`;

      logs.forEach((log, index) => {
        if (log.isSDKLog) {
          // SDK log format: Keep original format without extra time/level prefix
          logText += `[${log.time}][${log.level}][trro_web_sdk-${log.sdkVersion || 'N/A'}]`;
          if (log.module) logText += `[${log.module}]`;
          if (log.remoteDeviceId) logText += `[${log.remoteDeviceId}]`;
          if (log.fieldDeviceId) logText += `[${log.fieldDeviceId}]`;
          if (log.trackId) logText += `[${log.trackId}]`;
          logText += ` ${log.content}\n`;
        } else {
          // Regular log format: Check if content starts with '['
          const contentStartsWithBracket = log.content?.trim().startsWith('[');
          if (contentStartsWithBracket) {
            // No space after level if content starts with '['
            logText += `[${log.time}][${log.level}]${log.content}\n`;
          } else {
            // Add space after level if content doesn't start with '['
            logText += `[${log.time}][${log.level}] ${log.content}\n`;
          }
        }
      });

      // Create and download file
      const blob = new Blob([logText], { type: 'text/plain;charset=utf-8' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `trro-demo-logs-${SESSION_ID}-${Date.now()}.txt`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);

      alert(t('log_collector_export_success', { count: logs.length }));
    } catch (error) {
      console.error('[LogCollector] Failed to export logs:', error);
      alert(t('log_collector_export_failed', { error: error.message }));
    }
  }

  /**
   * Clear all logs for current session
   */
  async function clearLogs() {
    if (!confirm(t('log_collector_clear_confirm'))) {
      return;
    }

    try {
      await clearSessionLogs();
      logSequence = 0;
      updateLogCount();
      alert(t('log_collector_clear_success'));
    } catch (error) {
      console.error('[LogCollector] Failed to clear logs:', error);
      alert(t('log_collector_clear_failed', { error: error.message }));
    }
  }

  /**
   * Update log count display
   */
  async function updateLogCount() {
    try {
      const logs = await getSessionLogs();
      const countElem = document.getElementById('log-count');
      if (countElem) {
        countElem.textContent = logs.length;
      }
    } catch (error) {
      console.error('[LogCollector] Failed to update log count:', error);
    }
  }

  // ==================== UI Components ====================
  /**
   * Create debug tool card UI
   */
  function createDebugToolCard() {
    // Find the main element to insert the card
    const main = document.querySelector('main');
    if (!main) {
      console.error('[LogCollector] Cannot find main element');
      return;
    }

    const card = document.createElement('div');
    card.className = 'w-full box-border p-[15px] border rounded bg-white shadow-lg flex flex-col gap-[15px]';

    card.innerHTML = `
      <div>
        <h5 
          class="font-bold text-sm cursor-pointer hover:text-blue-600 flex items-center gap-[5px]"
          onclick="TRROLogCollector.toggleCard();"
          id="log-collector-header"
        >
          <span id="log-collector-arrow">▶</span>
          <span data-i18n="log_collector_title"></span>
          <span class="text-xs text-gray-500 ml-2 overflow-hidden text-ellipsis whitespace-nowrap" style="max-width: 400px;">
            (<span data-i18n="log_collector_session_id"></span>: ${SESSION_ID} | 
            <span data-i18n="log_collector_log_count"></span>: <span id="log-count">0</span>)
          </span>
        </h5>
      </div>
      <div id="log-collector-content" style="display: none;" class="flex flex-col gap-[15px]">
        <div class="text-sm text-gray-600 flex flex-col gap-[5px]">
          <p data-i18n="log_collector_description_1"></p>
          <p data-i18n="log_collector_description_2"></p>
          <p data-i18n="log_collector_description_3"></p>
        </div>
        <div class="flex gap-[10px] flex-wrap">
          <button 
            id="export-logs-btn" 
            class="bg-white p-[5px] border rounded text-blue-500 hover:text-blue-600 cursor-pointer"
            data-i18n="log_collector_button_export"
          ></button>
          <button 
            id="clear-logs-btn" 
            class="bg-white p-[5px] border rounded text-blue-500 hover:text-blue-600 cursor-pointer"
            data-i18n="log_collector_button_clear"
          ></button>
          <button 
            id="refresh-log-count-btn" 
            class="bg-white p-[5px] border rounded text-blue-500 hover:text-blue-600 cursor-pointer"
            data-i18n="log_collector_button_refresh"
          ></button>
        </div>
        <div class="text-xs text-gray-500">
          <p data-i18n="log_collector_hint"></p>
        </div>
      </div>
    `;

    // Append to the end of main
    main.appendChild(card);

    // Initialize i18n content
    updateCardI18n();

    // Event listeners
    document.getElementById('export-logs-btn').addEventListener('click', exportLogs);
    document.getElementById('clear-logs-btn').addEventListener('click', clearLogs);
    document.getElementById('refresh-log-count-btn').addEventListener('click', updateLogCount);

    // Update log count periodically
    setInterval(updateLogCount, 5000);

    // Hook into global i18n update
    if (typeof window.updatePageContent === 'function') {
      const originalUpdate = window.updatePageContent;
      window.updatePageContent = function () {
        originalUpdate();
        updateCardI18n();
      };
    }
  }

  /**
   * Update card i18n content
   */
  function updateCardI18n() {
    const card = document.getElementById('log-collector-header')?.closest('.w-full');
    if (!card) return;

    const elements = card.querySelectorAll('[data-i18n]');
    elements.forEach((element) => {
      const key = element.getAttribute('data-i18n');
      if (key && typeof t === 'function') {
        element.textContent = t(key);
      }
    });
  }

  /**
   * Toggle card visibility
   */
  function toggleCard() {
    const content = document.getElementById('log-collector-content');
    const arrow = document.getElementById('log-collector-arrow');

    if (content && arrow) {
      if (content.style.display === 'none') {
        content.style.display = 'flex';
        arrow.textContent = '▼';
      } else {
        content.style.display = 'none';
        arrow.textContent = '▶';
      }
    }
  }

  // ==================== Cleanup on Window Close ====================
  /**
   * Cleanup logs when window is closed or refreshed
   * Multiple strategies to ensure cleanup:
   * 1. beforeunload: Synchronous cleanup attempt
   * 2. visibilitychange: Cleanup when tab becomes hidden
   * 3. pagehide: iOS Safari fallback
   */

  // Strategy 1: beforeunload event
  window.addEventListener('beforeunload', (event) => {
    clearSessionLogsSync();
  });

  // Strategy 2: visibilitychange event (more reliable for modern browsers)
  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') {
      // When page becomes hidden (tab closed, switched, etc.)
      clearSessionLogsSync();
      // Also try async version as backup
      clearSessionLogs().catch(() => {});
    }
  });

  // Strategy 3: pagehide event (iOS Safari and other browsers)
  window.addEventListener('pagehide', (event) => {
    clearSessionLogsSync();
  });

  // ==================== Initialization ====================
  /**
   * Initialize log collector
   */
  async function init() {
    try {
      // Initialize database
      await initDB();

      // Hook console methods
      hookConsole();

      // Wait for DOM to be ready
      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
          createDebugToolCard();
          updateLogCount();
        });
      } else {
        createDebugToolCard();
        updateLogCount();
      }

      // Periodic cleanup check
      setInterval(checkLogCount, 60000); // Check every minute

      console.log('[LogCollector] Initialized successfully, Session ID:', SESSION_ID);
    } catch (error) {
      console.error('[LogCollector] Failed to initialize:', error);
    }
  }

  // Start initialization
  init();

  // Expose functions to global scope for debugging
  window.TRROLogCollector = {
    getSessionId: () => SESSION_ID,
    getSessionLogs,
    clearSessionLogs,
    exportLogs,
    clearLogs,
    updateLogCount,
    toggleCard,
  };
})();
