/// Thin service-worker entry: loads WASM, constructs the router state, and
/// forwards every message into router/index.route().

import type { Request, Response, SessionTimeoutConfig } from '../shared/messages';
import type { RouterState } from './router/index';
import { route } from './router/index';
import { encodeBinary, decodeBinary } from '../shared/message-binary';
import * as vault from './vault';
import { clearCurrent } from './session';
import * as sessionTimer from './session-timer';
import { READ_ONLY_CONTENT_CALLABLE } from './session-timer';

// @ts-ignore TS2307 — resolved by webpack alias / copy
import initDefault, { initSync } from '../../wasm/relicario_wasm.js';
// @ts-ignore TS2307
import * as wasmBindings from '../../wasm/relicario_wasm.js';

type WasmModule = typeof wasmBindings;
let wasm: WasmModule | null = null;

async function initWasm(): Promise<WasmModule> {
  if (wasm) return wasm;

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const SWGlobalScope = (globalThis as any).ServiceWorkerGlobalScope as (new () => ServiceWorker) | undefined;
  const isServiceWorker = typeof SWGlobalScope !== 'undefined'
    && self instanceof (SWGlobalScope as unknown as typeof EventTarget);

  if (isServiceWorker) {
    const wasmResponse = await fetch(chrome.runtime.getURL('relicario_wasm_bg.wasm'));
    const wasmBytes = await wasmResponse.arrayBuffer();
    initSync({ module: new WebAssembly.Module(wasmBytes) });
  } else {
    const wasmUrl = chrome.runtime.getURL('relicario_wasm_bg.wasm');
    await initDefault(wasmUrl);
  }

  vault.setWasm(wasmBindings);
  wasm = wasmBindings;
  return wasm;
}

// Single router-state object shared by all messages for this SW instance.
const state: RouterState = {
  manifest: null,
  gitHost: null,
  wasm: null,
};

// --- Session timer wiring ---

sessionTimer.onExpired(() => {
  // eslint-disable-next-line no-console
  console.log('[relicario sw] session expired — locking vault');
  clearCurrent();
  state.manifest = null;
  // Plan C Phase 5: don't leak the cached git-host client across a lock.
  // The initializer rebuilds gitHost on demand, so clearing here is safe.
  state.gitHost = null;
  // Best-effort broadcast — receiver may not exist yet.
  chrome.runtime.sendMessage({ type: 'session_expired' }).catch(() => {});
});

// Restore saved session config from chrome.storage.local on SW startup.
chrome.storage.local.get('session_timeout').then((r) => {
  if (r.session_timeout) {
    sessionTimer.setConfig(r.session_timeout as SessionTimeoutConfig);
  }
}).catch(() => {});

chrome.commands.onCommand.addListener((command) => {
  if (command === 'open-vault') {
    chrome.tabs.create({ url: chrome.runtime.getURL('vault.html') });
  }
});

chrome.runtime.onMessage.addListener(
  (rawRequest: Request, sender: chrome.runtime.MessageSender, sendResponse: (r: Response) => void) => {
    // Binary payloads (carrier/reference images, attachments, backups) cross
    // the channel as base64 envelopes — decode before routing. See message-binary.ts.
    const request = decodeBinary(rawRequest);
    (async () => {
      // Plan C Phase 5: invert the reset rule. Reset on every message
      // except a documented passive-read exclusion set, so an active
      // autofiller / content-driven flow keeps the vault alive.
      if (!READ_ONLY_CONTENT_CALLABLE.has(request.type)) {
        sessionTimer.resetTimer();
      }
      if (!state.wasm) {
        // eslint-disable-next-line no-console
        console.log('[relicario sw] initializing WASM on first message');
        state.wasm = await initWasm();
      }
      return route(request, state, sender);
    })()
      .then((r) => {
        if (!r.ok) {
          // eslint-disable-next-line no-console
          console.warn(`[relicario sw] ${request.type} -> error:`, r.error);
        }
        // Re-encode binary in the response so it survives the channel back.
        sendResponse(encodeBinary(r));
      })
      .catch((err: Error) => {
        // eslint-disable-next-line no-console
        console.error(`[relicario sw] ${request.type} threw:`, err);
        sendResponse({ ok: false, error: err.message });
      });
    return true; // async response
  },
);
