/// Content script entry point.
///
/// Detects login forms on the page by finding password fields and their
/// associated username inputs. Injects small icons into detected fields
/// and sets up a fill listener to receive credentials from the service worker.

import { setupFillListener } from './fill';
import { injectFieldIcons } from './icon';
import { hookForms } from './capture';

/// Find password fields on the page and detect their associated username inputs.
function detectLoginForms(): Array<{ password: HTMLInputElement; username: HTMLInputElement | null }> {
  const passwordFields = document.querySelectorAll<HTMLInputElement>('input[type="password"]');
  const forms: Array<{ password: HTMLInputElement; username: HTMLInputElement | null }> = [];

  for (const pwField of passwordFields) {
    // Skip hidden or very small fields (likely honeypots).
    if (pwField.offsetWidth < 20 || pwField.offsetHeight < 10) continue;

    const username = findUsernameField(pwField);
    forms.push({ password: pwField, username });
  }

  return forms;
}

/// Find the most likely username field associated with a password field.
///
/// Priority:
/// 1. autocomplete="username" in the same form
/// 2. autocomplete="email" in the same form
/// 3. type="email" in the same form
/// 4. name/id matching /user|email|login|account/i in the same form
/// 5. Nearest preceding visible text input (sibling or DOM-adjacent)
function findUsernameField(pwField: HTMLInputElement): HTMLInputElement | null {
  const form = pwField.closest('form');
  const scope = form ?? document;
  const inputs = scope.querySelectorAll<HTMLInputElement>('input');

  // 1. autocomplete="username"
  for (const input of inputs) {
    if (input === pwField) continue;
    if (input.autocomplete === 'username') return input;
  }

  // 2. autocomplete="email"
  for (const input of inputs) {
    if (input === pwField) continue;
    if (input.autocomplete === 'email') return input;
  }

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

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

  // 5. Nearest preceding visible text 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;
}

/// Scan the page for login forms and inject icons.
function scan(): void {
  const forms = detectLoginForms();
  for (const { password, username } of forms) {
    injectFieldIcons(password, username);
  }
  hookForms();
}

// --- Initialization ---

// Set up the fill listener (receives credentials from service worker).
setupFillListener();

// Initial scan.
scan();

// Watch for DOM changes (SPA navigation, dynamically loaded forms).
// Plan C Phase 5: SPA churn fires the MutationObserver many times per
// second. Trailing-edge debounce coalesces bursts so we run the full
// scan() at most once per quiet 200ms window.
const SCAN_DEBOUNCE_MS = 200;
let scanTimer: ReturnType<typeof setTimeout> | undefined;

function scheduleScan(): void {
  if (scanTimer !== undefined) clearTimeout(scanTimer);
  scanTimer = setTimeout(() => {
    scanTimer = undefined;
    scan();
  }, SCAN_DEBOUNCE_MS);
}

const observer = new MutationObserver(scheduleScan);

observer.observe(document.body, {
  childList: true,
  subtree: true,
});
