// Minimal MV3 background service worker + side panel opener.
if (chrome?.runtime?.onInstalled) {
  chrome.runtime.onInstalled.addListener(() => {
  console.log('Miner Wars Simulator extension installed');

  // Ensure side panel defaults to our redesign page (for tabs that support the API)
  if (chrome.sidePanel && chrome.sidePanel.setOptions) {
    chrome.tabs.query({}, (tabs) => {
      tabs?.forEach((t) => {
        try {
          chrome.sidePanel.setOptions({ tabId: t.id, path: 'index.html', enabled: true }).catch(() => {});
        } catch (err) {
          console.warn('[MW bg] setOptions failed on install', err?.message);
        }
      });
    });
  }

  // If available, set Chrome to open the side panel automatically on action click
  if (chrome.sidePanel && chrome.sidePanel.setPanelBehavior) {
    try {
      chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
    } catch (err) {
      console.warn('[MW bg] setPanelBehavior failed', err?.message);
    }
  }
  scheduleLiveApiAlarm('install');
  scheduleUiRefreshAlarm('install');
  });
}

let popupPort = null;
let lastContentTabId = null;
const contentPorts = new Map();
const pendingContentCommands = new Map();
let nextCommandId = 1;
let lastWsSnapshotWriteAt = 0;

function logBackground(...args) {
  console.log('[MW bg]', ...args);
}

const LIVE_API_ALARM_NAME = 'mw_live_api_refresh';
const LIVE_API_ALARM_MIN_MS = 300000;
const LIVE_API_ALARM_MAX_MS = 400000;
const UI_REFRESH_ALARM_NAME = 'mw_ui_refresh';
const UI_REFRESH_ALARM_MIN_MS = 20000;
const UI_REFRESH_ALARM_MAX_MS = 30000;

function scheduleLiveApiAlarm(reason = '') {
  if (!chrome?.alarms?.create) return;
  const span = Math.max(0, LIVE_API_ALARM_MAX_MS - LIVE_API_ALARM_MIN_MS);
  const delayMs = LIVE_API_ALARM_MIN_MS + Math.floor(Math.random() * (span + 1));
  chrome.alarms.create(LIVE_API_ALARM_NAME, { delayInMinutes: delayMs / 60000 });
  logBackground(`Scheduled live API alarm${reason ? ` (${reason})` : ''}`, delayMs);
  chrome.runtime.sendMessage({
    type: 'MW_LIVE_API_ALARM_SCHEDULED',
    nextAt: Date.now() + delayMs,
    delayMs
  }).catch(() => {});
}

function clearLiveApiAlarm() {
  if (!chrome?.alarms?.clear) return;
  chrome.alarms.clear(LIVE_API_ALARM_NAME);
}

function scheduleUiRefreshAlarm(reason = '') {
  if (!chrome?.alarms?.create) return;
  const span = Math.max(0, UI_REFRESH_ALARM_MAX_MS - UI_REFRESH_ALARM_MIN_MS);
  const delayMs = UI_REFRESH_ALARM_MIN_MS + Math.floor(Math.random() * (span + 1));
  chrome.alarms.create(UI_REFRESH_ALARM_NAME, { delayInMinutes: delayMs / 60000 });
  logBackground(`Scheduled UI refresh alarm${reason ? ` (${reason})` : ''}`, delayMs);
}

function clearUiRefreshAlarm() {
  if (!chrome?.alarms?.clear) return;
  chrome.alarms.clear(UI_REFRESH_ALARM_NAME);
}

if (chrome?.alarms?.onAlarm) {
  chrome.alarms.onAlarm.addListener((alarm) => {
    if (alarm?.name !== LIVE_API_ALARM_NAME) return;
    logBackground('Live API alarm fired', alarm?.scheduledTime || Date.now());
    chrome.runtime.sendMessage({ type: 'MW_LIVE_API_ALARM', at: Date.now() }).catch(() => {});
    scheduleLiveApiAlarm('reschedule');
  });
}

if (chrome?.alarms?.onAlarm) {
  chrome.alarms.onAlarm.addListener((alarm) => {
    if (alarm?.name !== UI_REFRESH_ALARM_NAME) return;
    chrome.runtime.sendMessage({ type: 'MW_UI_REFRESH', at: Date.now() }).catch(() => {});
    scheduleUiRefreshAlarm('reschedule');
  });
}

if (chrome?.runtime?.onStartup) {
  chrome.runtime.onStartup.addListener(() => {
    scheduleLiveApiAlarm('startup');
    scheduleUiRefreshAlarm('startup');
  });
}

function queryFallbackTab(callback) {
  chrome.tabs.query({ url: ['*://gomining.com/*miner-wars*', '*://*.gomining.com/*miner-wars*'] }, (tabs) => {
    const candidate = Array.isArray(tabs) && tabs.length ? tabs[0] : null;
    callback(candidate?.id || null);
  });
}

const COMMAND_TIMEOUT_MS = 15000;
const LONG_COMMAND_TIMEOUT_MS = 600000; // allow up to 10 minutes for heavy scans
const LONG_RUNNING_COMMANDS = new Set([
  'COLLECT_CLAN_URLS',
  'EXTRACT_CLAN_PAGE',
  'EXTRACT_CLAN_FROM_URL',
  'SCRAPE_CLAN_FROM_URL',
  'SCRAPE_LEADERBOARD_CLANS',
  'SCRAPE_ROUND_HISTORY',
  'NAVIGATE_TO_CLAN_URL',
  'RESTORE_PREVIOUS_URL'
]);

function getCommandTimeout(message = {}) {
  if (message?.type && LONG_RUNNING_COMMANDS.has(message.type)) {
    return LONG_COMMAND_TIMEOUT_MS;
  }
  return COMMAND_TIMEOUT_MS;
}

function clearPendingCommand(requestId) {
  const pending = pendingContentCommands.get(requestId);
  if (!pending) return;
  clearTimeout(pending.timeout);
  pendingContentCommands.delete(requestId);
}

function rejectPendingCommandsForTab(tabId) {
  for (const [requestId, entry] of pendingContentCommands.entries()) {
    if (entry.tabId === tabId) {
      clearTimeout(entry.timeout);
      entry.reject(new Error('Content tab disconnected'));
      pendingContentCommands.delete(requestId);
    }
  }
}

function sendCommandViaPort(tabId, message) {
  const entry = contentPorts.get(tabId);
  if (!entry) {
    return Promise.reject(new Error('No content script port available'));
  }
  const requestId = `cmd_${Date.now()}_${nextCommandId++}`;
  return new Promise((resolve, reject) => {
    const timeoutMs = getCommandTimeout(message);
    const timeout = setTimeout(() => {
      pendingContentCommands.delete(requestId);
      reject(new Error('Command timed out'));
    }, timeoutMs);
    pendingContentCommands.set(requestId, { resolve, reject, timeout, tabId });
    try {
      entry.port.postMessage({ type: 'COMMAND', requestId, payload: message });
    } catch (err) {
      clearTimeout(timeout);
      pendingContentCommands.delete(requestId);
      reject(err);
    }
  });
}

function injectContentScript(tabId, callback, reason = '') {
  if (!tabId || !chrome?.scripting?.executeScript) {
    callback?.(new Error('Cannot inject content script.'));
    return;
  }
  chrome.scripting.executeScript({
    target: { tabId },
    files: ['content-script.js']
  }, () => {
    if (chrome.runtime.lastError) {
      notifyHookInjectStatus('contentInjectError', {
        reason,
        message: chrome.runtime.lastError.message
      });
      callback?.(new Error(chrome.runtime.lastError.message));
      return;
    }
    notifyHookInjectStatus('contentInjected', { reason, tabId });
    callback?.();
  });
}

function ensureContentScript(tabId, reason = '') {
  if (!tabId || !chrome?.scripting?.executeScript) {
    return;
  }
  chrome.scripting.executeScript({
    target: { tabId },
    func: () => Boolean(globalThis.__MW_CONTENT_SCRIPT_LOADED__ || window.__MW_CONTENT_SCRIPT_LOADED__)
  }, (results) => {
    if (chrome.runtime.lastError) {
      notifyHookInjectStatus('contentCheckError', {
        reason,
        message: chrome.runtime.lastError.message
      });
      injectContentScript(tabId, () => {}, reason);
      return;
    }
    const alreadyLoaded = Boolean(results && results[0] && results[0].result);
    if (alreadyLoaded) {
      notifyHookInjectStatus('contentAlready', { reason, tabId });
      return;
    }
    injectContentScript(tabId, () => {}, reason);
  });
}

function notifyHookInjectStatus(status, detail) {
  chrome.runtime.sendMessage({
    type: 'MW_WS_DEBUG',
    eventName: `pageHook:${status}`,
    payload: detail,
    at: Date.now()
  }).catch(() => {});
}

function ensureMiningWarsHooks(reason = 'manual') {
  if (!chrome?.tabs?.query) return;
  chrome.tabs.query({ url: ['https://gomining.com/*', 'https://*.gomining.com/*'] }, (tabs) => {
    if (!Array.isArray(tabs) || !tabs.length) {
      notifyHookInjectStatus('ensureNone', { reason, tabs: 0 });
      return;
    }
    const targetTabs = tabs.filter((tab) => /\/miner-wars(\/|$)/i.test(tab.url || ''));
    if (!targetTabs.length) {
      notifyHookInjectStatus('ensureNone', { reason, tabs: 0 });
      return;
    }
    targetTabs.forEach((tab) => {
      if (!tab?.id) return;
      notifyHookInjectStatus('ensureAttempt', { reason, tabId: tab.id });
      ensureContentScript(tab.id, reason);
      injectPageHook(tab.id, 'ensure');
    });
  });
}

function injectPageHook(tabId, reason = '') {
  if (!tabId || !chrome?.scripting?.executeScript) {
    return;
  }
  notifyHookInjectStatus('injectAttempt', { reason, tabId });
  chrome.scripting.executeScript({
    target: { tabId, allFrames: true },
    files: ['page-hook.js'],
    world: 'MAIN'
  }, () => {
    if (chrome.runtime.lastError) {
      notifyHookInjectStatus('injectError', {
        reason,
        message: chrome.runtime.lastError.message
      });
      return;
    }
    notifyHookInjectStatus('injected', { reason });
  });
}

function sendToContentTab(tabId, message, onSuccess, onError, attemptedInjection = false) {
  if (!tabId) {
    onError?.(new Error('No tab id specified'));
    return;
  }
  const portEntry = contentPorts.get(tabId);
  if (portEntry) {
    logBackground('Sending command via port', tabId, message.type);
    sendCommandViaPort(tabId, message)
      .then((result) => {
        logBackground('Command response from port', tabId, message.type);
        onSuccess({ ok: true, data: result });
      })
      .catch((err) => {
        logBackground('Port send failed', tabId, err?.message);
        onError?.(err);
      });
    return;
  }
  logBackground('Sending message to content tab', tabId, message.type);
  chrome.tabs.sendMessage(tabId, message, (response) => {
    if (chrome.runtime.lastError) {
      const errMsg = chrome.runtime.lastError.message || '';
      logBackground('sendMessage error', errMsg);
      if (!attemptedInjection && errMsg.includes('Receiving end does not exist')) {
        logBackground('Injecting content script due to missing listener', tabId);
        injectContentScript(tabId, (injectErr) => {
          if (injectErr) {
            logBackground('Injection failed', injectErr?.message);
            onError?.(injectErr);
            return;
          }
          // Retry the send once after injection.
          sendToContentTab(tabId, message, onSuccess, onError, true);
        });
        return;
      }
      onError?.(new Error(errMsg));
      return;
    }
    logBackground('Received response from tab', tabId, response);
    onSuccess(response);
  });
}

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg && msg.type === 'MW_LIVE_API_ALARM_START') {
    logBackground('Live API alarm start requested');
    scheduleLiveApiAlarm('start');
    sendResponse?.({ ok: true });
    return false;
  }
  if (msg && msg.type === 'MW_LIVE_API_ALARM_STOP') {
    logBackground('Live API alarm stop requested');
    clearLiveApiAlarm();
    sendResponse?.({ ok: true });
    return false;
  }
  if (msg && msg.type === 'MW_AUTH_GOOGLE') {
    if (!chrome.identity?.launchWebAuthFlow) {
      sendResponse({ ok: false, error: 'Chrome identity API not available.' });
      return false;
    }
    chrome.identity.launchWebAuthFlow({ url: msg.url, interactive: true }, (redirectedTo) => {
      if (chrome.runtime.lastError) {
        sendResponse({ ok: false, error: chrome.runtime.lastError.message });
        return;
      }
      sendResponse({ ok: true, redirectUrl: redirectedTo });
    });
    return true;
  }
  if (msg && msg.type === 'REGISTER_CONTENT_SCRIPT' && sender?.tab?.id) {
    lastContentTabId = sender.tab.id;
    logBackground('Content script registered from tab', sender.tab.id);
    sendResponse?.({ ok: true });
    return false;
  }

  if (msg && msg.type === 'MW_ENSURE_HOOK') {
    ensureMiningWarsHooks('panel-request');
    sendResponse?.({ ok: true });
    return false;
  }

  // Forward API monitoring messages to extension/side panel
  if (msg && msg.type === 'MW_API_CALL') {
    // Broadcast to side panel if it's open
    chrome.runtime.sendMessage(msg).catch(() => {
      // Side panel might not be open, that's OK
    });
    return false;
  }

  if (msg && (msg.type === 'EXTRACT_LEADERBOARD' || msg.type === 'EXTRACT_CLAN_PAGE' || msg.type === 'SCRAPE_LEADERBOARD_CLANS' || msg.type === 'SCAN_KEEP_ALIVE' || msg.type === 'COLLECT_CLAN_URLS' || msg.type === 'EXTRACT_CLAN_FROM_URL' || msg.type === 'SCRAPE_CLAN_FROM_URL' || msg.type === 'SCRAPE_ROUND_HISTORY' || msg.type === 'NAVIGATE_TO_CLAN_URL' || msg.type === 'RESTORE_PREVIOUS_URL')) {
    logBackground('Extraction request from tab', sender?.tab?.id, msg.type);
    if (msg.type === 'SCAN_KEEP_ALIVE') {
      sendResponse({ ok: true });
      return false;
    }
    const handleFailure = (err) => {
      logBackground('Extraction send failed:', err?.message);
      queryFallbackTab((fallbackTabId) => {
        if (fallbackTabId && fallbackTabId !== (lastContentTabId || sender?.tab?.id)) {
          logBackground('Trying fallback tab', fallbackTabId);
          sendToContentTab(fallbackTabId, msg, sendResponse, (fallbackErr) => {
            logBackground('Fallback send failed', fallbackErr?.message);
            sendResponse({ ok: false, error: fallbackErr?.message || 'Failed to reach content script.' });
          });
          return;
        }
        sendResponse({ ok: false, error: err?.message || 'No content script tab is available.' });
      });
    };
    const primaryTab = lastContentTabId || sender?.tab?.id;
    sendToContentTab(primaryTab, msg, sendResponse, handleFailure);
    return true;
  }

  if (msg && msg.type === 'OPEN_SIDE_PANEL' && sender.tab && chrome.sidePanel) {
    chrome.sidePanel.open({ tabId: sender.tab.id }).catch(() => {});
  }

  if (msg && msg.type === 'INJECT_HOOK' && sender.tab && sender.tab.id) {
    try {
      injectPageHook(sender.tab.id, 'content-request');
    } catch (err) {
      console.warn('[MW] Failed to inject page-hook via scripting', err);
    }
  }

  // Fallback relay for boost/round events via sendMessage
  if (msg && msg.type === 'MW_EVENT_FROM_CONTENT') {
    if (popupPort) {
      popupPort.postMessage({ action: 'newLogEntry', data: msg.data });
    } else {
      chrome.runtime.sendMessage({ action: 'newLogEntry', data: msg.data }).catch(() => {});
    }
  }

  if (msg && msg.type === 'MW_WS_FROM_CONTENT') {
    console.log('[MW bg] relay MW_WS_FROM_CONTENT to popup', !!popupPort);
    if (popupPort) {
      popupPort.postMessage({ action: 'wsEvent', data: msg.data });
    }
    chrome.runtime.sendMessage({ action: 'wsEvent', data: msg.data }).catch(() => {});
    try {
      const rawEvent = Array.isArray(msg.data?.rawEvent) ? msg.data.rawEvent : null;
      const eventName = rawEvent?.[0];
      const eventData = rawEvent?.[1];
      const now = Date.now();
      if (eventName && String(eventName).toLowerCase().includes('allusersstatev2') && Array.isArray(eventData)) {
        if (now - lastWsSnapshotWriteAt > 1000) {
          lastWsSnapshotWriteAt = now;
          chrome.storage.local.set({
            mw_lastAllUsersStateV2Raw: eventData,
            mw_lastAllUsersStateV2At: now
          }).catch(() => {});
        }
      }
    } catch (_) {}
  }

  if (msg && msg.type === 'MW_WS_DEBUG') {
    if (popupPort) {
      popupPort.postMessage({ action: 'wsDebug', data: msg });
    }
    chrome.runtime.sendMessage(msg).catch(() => {});
  }

  if (msg && msg.type === 'MW_ROSTER_FROM_CONTENT') {
    if (popupPort) {
      popupPort.postMessage({ action: 'rosterUpdate', data: msg.data });
    }
    chrome.runtime.sendMessage({ action: 'rosterUpdate', data: msg.data }).catch(() => {});
  }

  if (msg && msg.type === 'MW_LIVE_SUMMARY') {
    if (popupPort) {
      popupPort.postMessage({ action: 'liveSummary', data: msg.data });
    }
    chrome.runtime.sendMessage({ action: 'liveSummary', data: msg.data }).catch(() => {});
  }

  if (msg && msg.type === 'MW_LIVE_SCORE_SNAPSHOT') {
    if (popupPort) {
      popupPort.postMessage({ action: 'liveScoreSnapshot', data: msg.data });
    }
    chrome.runtime.sendMessage({ action: 'liveScoreSnapshot', data: msg.data }).catch(() => {});
  }

  if (msg && msg.type === 'MW_WINNER_CLAN_NAME') {
    if (popupPort) {
      popupPort.postMessage({ action: 'winnerClanName', data: msg.data });
    }
    chrome.runtime.sendMessage({ action: 'winnerClanName', data: msg.data }).catch(() => {});
  }

  if (msg && msg.type === 'MW_TOKEN_EXPIRED') {
    if (popupPort) {
      popupPort.postMessage({ action: 'tokenExpired', data: msg.data });
    }
    chrome.runtime.sendMessage({ action: 'tokenExpired', data: msg.data }).catch(() => {});
  }
});

// Keep a simple port open so the side panel can connect without errors.
chrome.runtime.onConnect.addListener((port) => {
  if (port.name === 'popup') {
    popupPort = port;
    port.postMessage({ action: 'liveStatus', data: 'connected' });
    port.onDisconnect.addListener(() => {
      popupPort = null;
    });
  }

  if (port.name === 'content') {
    const tabId = port.sender?.tab?.id;
    if (tabId) {
      contentPorts.set(tabId, { port });
      lastContentTabId = tabId;
    }
    port.onMessage.addListener((msg) => {
      if (!msg) return;
      if (msg.type === 'COMMAND_RESPONSE' && msg.requestId) {
        const pending = pendingContentCommands.get(msg.requestId);
        if (pending) {
          clearPendingCommand(msg.requestId);
          if (msg.ok) {
            pending.resolve(msg.data);
          } else {
            pending.reject(new Error(msg.error || 'Command failed'));
          }
        }
        return;
      }
      if (msg && msg.action === 'boostEvent') {
        if (popupPort) {
          popupPort.postMessage({ action: 'newLogEntry', data: msg.data });
        }
      }
      if (msg && msg.action === 'wsEvent' && popupPort) {
        console.log('[MW bg] relay wsEvent from port to popup', msg.data && msg.data.rawEvent ? msg.data.rawEvent[0] : Object.keys(msg.data || {}));
        popupPort.postMessage({ action: 'wsEvent', data: msg.data });
      }
      if (msg && msg.action === 'wsEvent') {
        chrome.runtime.sendMessage({ action: 'wsEvent', data: msg.data }).catch(() => {});
        try {
          const rawEvent = Array.isArray(msg.data?.rawEvent) ? msg.data.rawEvent : null;
          const eventName = rawEvent?.[0];
          const eventData = rawEvent?.[1];
          const now = Date.now();
          if (eventName && String(eventName).toLowerCase().includes('allusersstatev2') && Array.isArray(eventData)) {
            if (now - lastWsSnapshotWriteAt > 1000) {
              lastWsSnapshotWriteAt = now;
              chrome.storage.local.set({
                mw_lastAllUsersStateV2Raw: eventData,
                mw_lastAllUsersStateV2At: now
              }).catch(() => {});
            }
          }
        } catch (_) {}
      }
      if (msg && msg.action === 'rosterUpdate') {
        if (popupPort) {
          popupPort.postMessage({ action: 'rosterUpdate', data: msg.data });
        }
        chrome.runtime.sendMessage({ action: 'rosterUpdate', data: msg.data }).catch(() => {});
      }
      if (msg && msg.action === 'liveSummary') {
        if (popupPort) {
          popupPort.postMessage({ action: 'liveSummary', data: msg.data });
        }
        chrome.runtime.sendMessage({ action: 'liveSummary', data: msg.data }).catch(() => {});
      }
      if (msg && msg.action === 'winnerClanName') {
        if (popupPort) {
          popupPort.postMessage({ action: 'winnerClanName', data: msg.data });
        }
        chrome.runtime.sendMessage({ action: 'winnerClanName', data: msg.data }).catch(() => {});
      }
      if (msg && msg.action === 'tokenExpired') {
        if (popupPort) {
          popupPort.postMessage({ action: 'tokenExpired', data: msg.data });
        }
        chrome.runtime.sendMessage({ action: 'tokenExpired', data: msg.data }).catch(() => {});
      }
      if (msg && msg.action === 'ping' && popupPort) {
        popupPort.postMessage({ action: 'liveStatus', data: 'pong' });
      }
    });
    port.onDisconnect.addListener(() => {
      if (tabId) {
        contentPorts.delete(tabId);
        rejectPendingCommandsForTab(tabId);
        if (lastContentTabId === tabId) {
          lastContentTabId = Array.from(contentPorts.keys()).pop() || null;
        }
      }
    });
  }
});

if (chrome?.tabs?.onUpdated) {
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    if (!tabId || !tab?.url) return;
    if (!/\/miner-wars(\/|$)/i.test(tab.url)) return;
    if (!/(^|\\.)gomining\\.com$/i.test(new URL(tab.url).hostname)) return;
    if (changeInfo.status === 'complete') {
      ensureContentScript(tabId, 'tab-updated');
      injectPageHook(tabId, 'tab-updated');
    }
  });
}

if (chrome?.runtime?.onStartup) {
  chrome.runtime.onStartup.addListener(() => {
    ensureMiningWarsHooks('startup');
  });
}

chrome.action.onClicked.addListener((tab) => {
  console.log('[MW bg] action clicked', tab?.id);
  // Prefer side panel when available; otherwise open popup.
  if (chrome.sidePanel && tab?.id) {
    chrome.sidePanel.setOptions({ tabId: tab.id, path: 'index.html', enabled: true })
      .then(() => chrome.sidePanel.open({ tabId: tab.id }))
      .catch((err) => {
        console.warn('[MW bg] sidePanel failed, opening popup:', err?.message);
        openFallbackPopup();
      });
  } else {
    openFallbackPopup();
  }
});
