/// Single chrome.runtime.onMessage entry. Classifies the sender and dispatches
/// to popup-only or content-callable handlers. Unauthorized senders are
/// rejected with { ok: false, error: 'unauthorized_sender' }.

import type { PopupMessage, Request, Response } from '../../shared/messages';
import { POPUP_ONLY_TYPES, CONTENT_CALLABLE_TYPES } from '../../shared/messages';
import type { Manifest } from '../../shared/types';
import type { GitHost } from '../git-host';
import * as popupOnly from './popup-only';
import * as contentCallable from './content-callable';

export interface RouterState {
  manifest: Manifest | null;
  gitHost: GitHost | null;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  wasm: any;
}

/// Popup-only messages the setup tab is also allowed to send. The setup page
/// (setup.html) is an extension-internal page — same trust level as the popup —
/// so this set mirrors exactly what the wizard sends, no more.
/// - save_setup: wires vault config + image into chrome.storage.local at end of init.
/// - rate_passphrase: drives the zxcvbn strength meter during passphrase entry.
/// - is_unlocked: setup step-4 pings the extension to detect "save config to extension" availability.
/// - create_vault / attach_vault: the "name this device" step performs the full vault create/attach.
/// - generate_recovery_qr: the done-step recovery-QR banner generates a QR before the user leaves.
const SETUP_ALLOWED: ReadonlySet<PopupMessage['type']> = new Set<PopupMessage['type']>([
  'save_setup',
  'rate_passphrase',
  'is_unlocked',
  'create_vault',
  'attach_vault',
  'generate_recovery_qr',
]);

export async function route(
  msg: Request,
  state: RouterState,
  sender: chrome.runtime.MessageSender,
): Promise<Response> {
  const popupUrl = chrome.runtime.getURL('popup.html');
  const vaultUrl = chrome.runtime.getURL('vault.html');
  const setupUrl = chrome.runtime.getURL('setup.html');
  const senderUrl = sender.url ?? '';

  const isPopup   = senderUrl.startsWith(popupUrl) || senderUrl.startsWith(vaultUrl);
  const isSetup   = senderUrl.startsWith(setupUrl);
  const isContent = sender.tab !== undefined
                  && sender.frameId === 0
                  && sender.id === chrome.runtime.id;

  if (POPUP_ONLY_TYPES.has(msg.type as never)) {
    if (!(isPopup || (isSetup && SETUP_ALLOWED.has(msg.type as PopupMessage['type'])))) {
      // eslint-disable-next-line no-console
      console.warn('[relicario router] rejected popup-only message from wrong sender', {
        type: msg.type, senderUrl, isPopup, isSetup, isContent,
      });
      return { ok: false, error: 'unauthorized_sender' };
    }
    return popupOnly.handle(msg as never, state, sender);
  }

  if (CONTENT_CALLABLE_TYPES.has(msg.type as never)) {
    if (!isContent) {
      // eslint-disable-next-line no-console
      console.warn('[relicario router] rejected content-only message from wrong sender', {
        type: msg.type, senderUrl, isPopup, isSetup, isContent,
        frameId: sender.frameId, senderId: sender.id,
      });
      return { ok: false, error: 'unauthorized_sender' };
    }
    return contentCallable.handle(msg as never, state, sender);
  }

  // eslint-disable-next-line no-console
  console.warn('[relicario router] unknown message type', { type: (msg as { type: string }).type });
  return { ok: false, error: 'unknown_message_type' };
}
