/// Fill listener — receives credentials from the service worker popup flow,
/// verifies origin, and fills page fields.
///
/// TOCTOU mitigation: the popup captures its active tab at open time and
/// passes {capturedTabId, capturedUrl, expectedHost} to the SW. The SW
/// re-fetches the tab and checks the hostname against `capturedUrl` before
/// forwarding, but between the SW's chrome.tabs.sendMessage and our receipt
/// the page could navigate. We re-check `location.href.hostname ===
/// expectedHost` before typing credentials. If the page has navigated
/// (different origin now running the content script), reply with
/// `origin_changed` and do nothing.

/// Message shape forwarded by router/popup-only.ts#handleFillCredentials.
export interface FillMessage {
  type: 'fill_credentials';
  username: string;
  password: string;
  /// The hostname the SW validated the captured tab was on. The content
  /// script rejects delivery if the page has since navigated away.
  expectedHost: string;
}

/// Set up a listener for fill_credentials messages from the service worker.
export function setupFillListener(): void {
  chrome.runtime.onMessage.addListener(
    (
      message: FillMessage,
      _sender: chrome.runtime.MessageSender,
      sendResponse: (response: { ok: boolean; error?: string }) => void,
    ) => {
      if (message.type !== 'fill_credentials') return false;
      const currentHost = (() => {
        try { return new URL(location.href).hostname; } catch { return ''; }
      })();
      if (!currentHost || currentHost !== message.expectedHost) {
        sendResponse({ ok: false, error: 'origin_changed' });
        return false;
      }
      fillFields(message.username, message.password);
      sendResponse({ ok: true });
      return false;
    },
  );
}

/// Fill username and password fields on the page.
///
/// Finds the first visible password field and its associated username field,
/// then sets their values using the native setter trick for React/Vue compat.
export function fillFields(username: string, password: string): void {
  const pwField = document.querySelector<HTMLInputElement>('input[type="password"]');
  if (!pwField) return;

  // Set the password.
  setNativeValue(pwField, password);

  // Find the username field (same logic as detector).
  if (username) {
    const usernameField = findUsernameForFill(pwField);
    if (usernameField) {
      setNativeValue(usernameField, username);
    }
  }
}

/// Use the native HTMLInputElement.value setter to bypass React/Vue wrappers.
/// Then dispatch input and change events so the framework picks up the change.
function setNativeValue(input: HTMLInputElement, value: string): void {
  const nativeSetter = Object.getOwnPropertyDescriptor(
    HTMLInputElement.prototype,
    'value',
  )?.set;

  if (nativeSetter) {
    nativeSetter.call(input, value);
  } else {
    input.value = value;
  }

  input.dispatchEvent(new Event('input', { bubbles: true }));
  input.dispatchEvent(new Event('change', { bubbles: true }));
}

/// Find the username field associated with a password field (simplified version for fill).
function findUsernameForFill(pwField: HTMLInputElement): HTMLInputElement | null {
  const form = pwField.closest('form');
  const scope = form ?? document;
  const inputs = scope.querySelectorAll<HTMLInputElement>('input');

  // Priority: autocomplete > type=email > name pattern > preceding text input.
  for (const input of inputs) {
    if (input === pwField) continue;
    if (input.autocomplete === 'username' || input.autocomplete === 'email') return input;
  }

  for (const input of inputs) {
    if (input === pwField) continue;
    if (input.type === 'email') return input;
  }

  const pattern = /user|email|login|account/i;
  for (const input of inputs) {
    if (input === pwField || input.type === 'hidden' || input.type === 'password') continue;
    if (pattern.test(input.name) || pattern.test(input.id)) return input;
  }

  const allInputs = Array.from(inputs);
  const pwIndex = allInputs.indexOf(pwField);
  for (let i = pwIndex - 1; i >= 0; i--) {
    const input = allInputs[i];
    if (input.type === 'hidden' || input.type === 'password' || input.type === 'submit') continue;
    if (input.offsetWidth > 0 && input.offsetHeight > 0) return input;
  }

  return null;
}
