/// Inject a small "id" icon into password fields for quick autofill access.
///
/// Each injected icon and picker renders inside a closed Shadow DOM so
/// the host page cannot read or manipulate our UI.
///
/// Flow:
///   1. Icon click → chrome.runtime.sendMessage({ type: 'get_autofill_candidates' })
///      (router derives origin from sender.tab.url; no url on message).
///   2. Single candidate → get_credentials; if response is a
///      requires_ack variant, show an in-page TOFU hint instructing the
///      user to open the popup for ack. Otherwise, call fillFields()
///      directly — the content script IS the page origin, so no SW
///      round-trip for the fill itself.
///   3. Multiple candidates → show the picker inside a shadow root.
///
/// Note: fill_credentials is popup-only in the router. The icon click path
/// cannot and MUST NOT issue fill_credentials from content.

import type { AutofillCandidatesResponse, CredentialsResponse, Response } from '../shared/messages';
import type { ManifestEntry, ItemId } from '../shared/types';
import { createShadowHost, type ShadowSurface } from './shadow';
import { fillFields } from './fill';

/// Track which fields already have an injected icon.
const injected = new WeakSet<HTMLInputElement>();

/// The currently-open picker / TOFU hint, if any.
let currentOverlay: ShadowSurface | null = null;

function closeOverlay(): void {
  if (currentOverlay) {
    currentOverlay.destroy();
    currentOverlay = null;
  }
}

/// Inject a small blue "id" icon at the right edge of a password field.
export function injectFieldIcons(
  passwordField: HTMLInputElement,
  _usernameField: HTMLInputElement | null,
): void {
  if (injected.has(passwordField)) return;
  injected.add(passwordField);

  // Each icon gets its own shadow host so page CSS cannot reach it.
  const surface = createShadowHost();
  const { host, root } = surface;

  // Compute initial position from the password field's bounding rect and
  // reposition on scroll/resize. We keep things lightweight — exact
  // pixel-perfect tracking during layout churn is not required.
  function positionHost(): void {
    const rect = passwordField.getBoundingClientRect();
    host.style.cssText = [
      'position: fixed',
      `top: ${rect.top + rect.height / 2 - 10}px`,
      `left: ${rect.right - 28}px`,
      'z-index: 2147483646',
      'pointer-events: auto',
    ].join('; ');
  }
  positionHost();
  window.addEventListener('scroll', positionHost, true);
  window.addEventListener('resize', positionHost);

  const icon = document.createElement('div');
  icon.textContent = 'id';
  icon.setAttribute('role', 'button');
  icon.setAttribute('aria-label', 'Relicario autofill');
  icon.style.cssText = [
    'width: 20px', 'height: 20px', 'line-height: 20px',
    'text-align: center', 'font-size: 10px', 'font-weight: 700',
    'font-family: monospace', 'color: #fff', 'background: #7c5719',
    'border-radius: 3px', 'cursor: pointer', 'user-select: none',
    'box-sizing: border-box',
  ].join('; ');
  root.appendChild(icon);

  icon.addEventListener('click', async (e) => {
    e.preventDefault();
    e.stopPropagation();

    // Note: no `url` on message — router derives from sender.tab.url.
    const resp = await chrome.runtime.sendMessage({
      type: 'get_autofill_candidates',
    }) as Response;

    if (!resp || !resp.ok) return;
    const candidates = (resp as AutofillCandidatesResponse).data.candidates;
    if (candidates.length === 0) return;

    if (candidates.length === 1) {
      await handleSingleCandidate(candidates[0][0]);
    } else {
      showPicker(passwordField, candidates);
    }
  });
}

/// Fetch credentials for a single item and either fill immediately or
/// display the TOFU ack hint.
async function handleSingleCandidate(id: ItemId): Promise<void> {
  const credResp = await chrome.runtime.sendMessage({
    type: 'get_credentials',
    id,
  }) as Response;
  if (!credResp?.ok) return;

  const data = (credResp as CredentialsResponse).data;
  if ('requires_ack' in data && data.requires_ack) {
    showAckHint(data.hostname);
    return;
  }
  // Discriminated union: must be the {username, password} variant here.
  if ('username' in data && 'password' in data) {
    fillFields(data.username, data.password);
  }
}

/// Render a dropdown picker below the password field for selecting among
/// multiple candidates. The picker lives in its own closed Shadow DOM.
function showPicker(
  anchor: HTMLInputElement,
  candidates: Array<[ItemId, ManifestEntry]>,
): void {
  closeOverlay();
  const surface = createShadowHost();
  currentOverlay = surface;
  const { host, root } = surface;

  const rect = anchor.getBoundingClientRect();
  host.style.cssText = [
    'position: fixed',
    `top: ${rect.bottom + 4}px`,
    `left: ${rect.right - 180}px`,
    'z-index: 2147483647',
  ].join('; ');

  const picker = document.createElement('div');
  picker.style.cssText = [
    'background: #161b22', 'border: 1px solid #30363d',
    'border-radius: 6px', 'box-shadow: 0 4px 12px rgba(0,0,0,0.4)',
    'min-width: 180px', 'max-height: 200px', 'overflow-y: auto',
    "font-family: 'JetBrains Mono', monospace", 'font-size: 12px',
  ].join('; ');

  for (const [id, entry] of candidates) {
    const row = document.createElement('div');
    const label = entry.title + (/* user hint */ '');
    row.textContent = label;
    row.style.cssText = [
      'padding: 8px 12px', 'cursor: pointer', 'color: #c9d1d9',
      'border-bottom: 1px solid #21262d',
    ].join('; ');
    row.addEventListener('mouseenter', () => { row.style.background = '#21262d'; });
    row.addEventListener('mouseleave', () => { row.style.background = 'transparent'; });
    row.addEventListener('click', async (e) => {
      e.stopPropagation();
      closeOverlay();
      await handleSingleCandidate(id);
    });
    picker.appendChild(row);
  }

  root.appendChild(picker);

  // Close picker on outside click (scoped to document; shadow root blocks
  // composedPath for closed mode but the host element still shows up).
  const closeHandler = (e: MouseEvent): void => {
    if (e.target !== host) {
      closeOverlay();
      document.removeEventListener('click', closeHandler);
    }
  };
  setTimeout(() => document.addEventListener('click', closeHandler), 0);
}

/// TOFU origin-ack hint: credentials exist for this host but the user has
/// never explicitly acknowledged autofill here. Instruct them to open
/// Relicario to confirm — we do not (and cannot) fill until ack-autofill
/// has been called from the popup.
function showAckHint(hostname: string): void {
  closeOverlay();
  const surface = createShadowHost();
  currentOverlay = surface;
  const { host, root } = surface;

  host.style.cssText = [
    'position: fixed', 'top: 16px', 'right: 16px',
    'z-index: 2147483647',
  ].join('; ');

  const hint = document.createElement('div');
  hint.style.cssText = [
    'font-family: "SF Mono", "Fira Code", "Cascadia Code", monospace',
    'font-size: 12px', 'color: #c9d1d9', 'background: #161b22',
    'border: 1px solid #30363d', 'border-radius: 6px',
    'padding: 10px 14px', 'box-shadow: 0 4px 12px rgba(0,0,0,0.4)',
    'max-width: 320px', 'line-height: 1.5',
  ].join('; ');

  const title = document.createElement('div');
  title.style.cssText = 'font-weight: 700; margin-bottom: 4px; color: #d2ab43;';
  title.textContent = 'Relicario';
  hint.appendChild(title);

  const body = document.createElement('div');
  body.appendChild(document.createTextNode('First autofill on '));
  const hostSpan = document.createElement('strong');
  hostSpan.textContent = hostname;
  body.appendChild(hostSpan);
  body.appendChild(document.createTextNode(' — open Relicario to confirm.'));
  hint.appendChild(body);

  const close = document.createElement('div');
  close.textContent = '✕';
  close.style.cssText = [
    'position: absolute', 'top: 6px', 'right: 8px',
    'cursor: pointer', 'color: #8b949e', 'font-size: 14px',
  ].join('; ');
  close.addEventListener('click', closeOverlay);
  hint.style.position = 'relative';
  hint.appendChild(close);

  root.appendChild(hint);

  // Auto-dismiss after 8 seconds
  setTimeout(() => {
    if (currentOverlay === surface) closeOverlay();
  }, 8000);
}
