(function contentController() {
  'use strict';

  const browserApi = globalThis.browser || globalThis.chrome;
  if (!browserApi?.runtime?.sendMessage) {
    return;
  }

  const state = {
    matches: [],
    launcherHost: null,
    launcherShadow: null,
    launcherUpdate: null,
    saveHost: null,
    autoFilledEntryKey: '',
    lastDetectionSignature: '',
    lastDetectionAt: 0,
    lastCapturedCredential: null,
    lastCapturedAt: 0,
    lastUsername: '',
    lastUsernameAt: 0,
    notifyTimer: null,
  };

  function t(key, substitutions, fallback) {
    try {
      return browserApi.i18n?.getMessage(key, substitutions) || fallback || key;
    } catch (_error) {
      return fallback || key;
    }
  }

  function sendMessage(message) {
    if (globalThis.browser?.runtime?.sendMessage) {
      return browserApi.runtime.sendMessage(message);
    }
    return new Promise((resolve, reject) => {
      browserApi.runtime.sendMessage(message, (response) => {
        const error = globalThis.chrome?.runtime?.lastError;
        if (error) {
          reject(new Error(error.message));
          return;
        }
        resolve(response);
      });
    });
  }

  function isVisible(element) {
    if (!(element instanceof HTMLInputElement)) {
      return false;
    }
    const rect = element.getBoundingClientRect();
    const style = window.getComputedStyle(element);
    return (
      rect.width > 0
      && rect.height > 0
      && style.display !== 'none'
      && style.visibility !== 'hidden'
      && style.opacity !== '0'
    );
  }

  function inputSignature(input) {
    return [
      input?.name,
      input?.id,
      input?.placeholder,
      input?.autocomplete,
      input?.getAttribute?.('aria-label'),
      input?.getAttribute?.('data-testid'),
    ].join(' ').toLowerCase();
  }

  function passwordFields(root = document) {
    return Array.from(root.querySelectorAll('input[type="password"]'))
      .filter((input) => !input.disabled && !input.readOnly && isVisible(input));
  }

  function usernameCandidates(root) {
    return Array.from(root.querySelectorAll('input'))
      .filter((input) => {
        const type = String(input.type || 'text').toLowerCase();
        return ['text', 'email', 'tel'].includes(type)
          && !input.disabled
          && !input.readOnly
          && isVisible(input);
      });
  }

  function bestUsernameField(root, passwordField = null) {
    const candidates = usernameCandidates(root);
    return candidates
      .map((input) => {
        const signature = inputSignature(input);
        const autocomplete = String(input.autocomplete || '').toLowerCase();
        let score = 0;
        if (autocomplete === 'username') score += 120;
        if (String(input.type || '').toLowerCase() === 'email') score += 80;
        if (/(user|login|konto|account|mail|email|e-mail|benutzer)/.test(signature)) score += 70;
        if (/(search|suche|filter|coupon|promo)/.test(signature)) score -= 100;
        if (passwordField && input.compareDocumentPosition(passwordField) & Node.DOCUMENT_POSITION_FOLLOWING) {
          score += 25;
        }
        return { input, score };
      })
      .sort((left, right) => right.score - left.score)[0]?.input || null;
  }

  function bestPasswordField(root, purpose = 'fill') {
    const candidates = passwordFields(root);
    if (!candidates.length) {
      return null;
    }
    const preferredAutocomplete = purpose === 'capture' ? 'new-password' : 'current-password';
    return candidates.find((input) => String(input.autocomplete || '').toLowerCase() === preferredAutocomplete)
      || candidates.find((input) => String(input.autocomplete || '').toLowerCase() === 'current-password')
      || candidates[0];
  }

  function setFieldValue(input, value) {
    if (!(input instanceof HTMLInputElement)) {
      return;
    }
    const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
    if (setter) {
      setter.call(input, String(value || ''));
    } else {
      input.value = String(value || '');
    }
    input.dispatchEvent(new InputEvent('input', {
      bubbles: true,
      composed: true,
      inputType: 'insertText',
      data: null,
    }));
    input.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
  }

  function fillCredentials(payload, options = {}) {
    const passwordField = bestPasswordField(document, 'fill');
    const root = passwordField?.form || document;
    const usernameField = bestUsernameField(root, passwordField);
    const username = String(payload?.username || '');
    const secret = String(payload?.secret || '');
    const onlyEmpty = options.onlyEmpty === true;

    if (!passwordField && !usernameField) {
      return { ok: false, message: t('noLoginFields', undefined, 'Keine passenden Anmeldefelder gefunden.') };
    }
    if (usernameField && username && (!onlyEmpty || !usernameField.value)) {
      setFieldValue(usernameField, username);
    }
    if (passwordField && secret && (!onlyEmpty || !passwordField.value)) {
      setFieldValue(passwordField, secret);
    }
    passwordField?.focus({ preventScroll: true });
    return {
      ok: true,
      usernameFilled: Boolean(usernameField && username),
      passwordFilled: Boolean(passwordField && secret),
    };
  }

  function escapeHtml(value) {
    return String(value || '')
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }

  function removeLauncher() {
    if (state.launcherUpdate) {
      window.removeEventListener('scroll', state.launcherUpdate);
      window.removeEventListener('resize', state.launcherUpdate);
    }
    state.launcherHost?.remove();
    state.launcherHost = null;
    state.launcherShadow = null;
    state.launcherUpdate = null;
  }

  function launcherPosition(passwordField) {
    const rect = passwordField.getBoundingClientRect();
    const size = 30;
    const left = rect.right + size + 8 <= window.innerWidth
      ? rect.right + 6
      : Math.max(6, rect.right - size - 6);
    return {
      left: Math.round(left),
      top: Math.round(rect.top + Math.max(0, (rect.height - size) / 2)),
    };
  }

  async function fillSelectedEntry(entry) {
    const response = await sendMessage({ type: 'orbitone:get-entry-detail', entry });
    if (!response?.ok || !response.entry?.secret) {
      return;
    }
    fillCredentials(response.entry);
    state.launcherShadow?.querySelector('.ov-menu')?.setAttribute('hidden', '');
  }

  function renderLauncher(matches) {
    const passwordField = bestPasswordField(document, 'fill');
    if (!passwordField || !matches.length) {
      removeLauncher();
      return;
    }

    removeLauncher();
    const host = document.createElement('div');
    host.id = 'orbitone-vault-field-control';
    host.style.cssText = 'all:initial;position:fixed;inset:0;z-index:2147483646;pointer-events:none;';
    const shadow = host.attachShadow({ mode: 'closed' });
    const position = launcherPosition(passwordField);
    const iconUrl = browserApi.runtime.getURL('icons/icon-32.png');

    shadow.innerHTML = `
      <style>
        :host { all: initial; }
        * { box-sizing: border-box; letter-spacing: 0; }
        button { font: 13px/1.3 Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
        .ov-launcher {
          position: fixed; left: ${position.left}px; top: ${position.top}px;
          width: 30px; height: 30px; padding: 0; display: grid; place-items: center;
          border: 1px solid #bedcff; border-radius: 6px; background: #fff;
          box-shadow: 0 4px 14px rgba(21,39,64,.16); cursor: pointer; pointer-events: auto;
        }
        .ov-launcher:hover, .ov-launcher:focus-visible { border-color: #0875e1; outline: 2px solid rgba(8,117,225,.18); }
        .ov-launcher img { width: 20px; height: 20px; }
        .ov-menu {
          position: fixed; left: min(${position.left}px, calc(100vw - 322px)); top: ${position.top + 36}px;
          width: 316px; max-height: 280px; overflow: auto; padding: 7px;
          border: 1px solid #dfe5ec; border-radius: 7px; background: #fff;
          box-shadow: 0 14px 34px rgba(21,39,64,.18); pointer-events: auto;
          color: #162033; font: 13px/1.4 Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
        }
        .ov-title { padding: 6px 8px 8px; color: #5f6b7d; font-size: 11px; font-weight: 750; text-transform: uppercase; }
        .ov-entry {
          width: 100%; padding: 9px 8px; display: grid; gap: 2px; text-align: left;
          border: 0; border-radius: 5px; background: transparent; color: #162033; cursor: pointer;
        }
        .ov-entry:hover, .ov-entry:focus-visible { background: #eaf3ff; outline: none; }
        .ov-entry strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .ov-entry span { overflow: hidden; color: #5f6b7d; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
      </style>
      <button class="ov-launcher" type="button" title="OrbitOne Vault" aria-label="OrbitOne Vault">
        <img src="${iconUrl}" alt="">
      </button>
      <div class="ov-menu" hidden>
        <div class="ov-title">${escapeHtml(t(matches.length === 1 ? 'matchingLogin' : 'matchingLogins', [String(matches.length)], `${matches.length} passende ${matches.length === 1 ? 'Anmeldung' : 'Anmeldungen'}`))}</div>
        ${matches.map((entry, index) => `
          <button class="ov-entry" type="button" data-entry-index="${index}">
            <strong>${escapeHtml(entry.title || entry.username || t('credential', undefined, 'Zugang'))}</strong>
            <span>${escapeHtml(entry.username || t('noUsername', undefined, 'Kein Benutzername'))} · ${escapeHtml([entry.tenantName || entry.connectionName, entry.workspaceName].filter(Boolean).join(' / '))}</span>
          </button>
        `).join('')}
      </div>
    `;

    const menu = shadow.querySelector('.ov-menu');
    shadow.querySelector('.ov-launcher')?.addEventListener('click', () => {
      menu?.toggleAttribute('hidden');
    });
    shadow.querySelectorAll('[data-entry-index]').forEach((button) => {
      button.addEventListener('click', () => {
        const entry = matches[Number(button.getAttribute('data-entry-index') || 0)];
        fillSelectedEntry(entry).catch(() => null);
      });
    });

    (document.body || document.documentElement).appendChild(host);
    state.launcherHost = host;
    state.launcherShadow = shadow;

    const update = () => {
      if (!state.launcherHost?.isConnected || !isVisible(passwordField)) {
        return;
      }
      const next = launcherPosition(passwordField);
      const launcher = shadow.querySelector('.ov-launcher');
      if (launcher) {
        launcher.style.left = `${next.left}px`;
        launcher.style.top = `${next.top}px`;
      }
    };
    window.addEventListener('scroll', update, { passive: true });
    window.addEventListener('resize', update, { passive: true });
    state.launcherUpdate = update;
  }

  function removeSavePrompt(clearPending = false) {
    state.saveHost?.remove();
    state.saveHost = null;
    if (clearPending) {
      sendMessage({ type: 'orbitone:clear-pending' }).catch(() => null);
    }
  }

  function selectDestination(connections, pending, defaults) {
    const connection = connections.find((row) => row.connectionId === pending?.connectionId)
      || connections.find((row) => row.connectionId === defaults?.activeConnectionId)
      || connections[0]
      || null;
    const workspaces = Array.isArray(connection?.workspaces) ? connection.workspaces : [];
    const pendingMatchesConnection = pending?.connectionId === connection?.connectionId;
    const preferredWorkspace = Number(
      (pendingMatchesConnection ? pending?.workspaceId : 0)
      || connection?.defaultWorkspaceId
      || defaults?.defaultWorkspaceId
      || 0
    );
    const preferredScope = pendingMatchesConnection && ['personal', 'workspace'].includes(pending?.scope)
      ? pending.scope
      : ((connection?.defaultScope || defaults?.defaultScope) === 'workspace' ? 'workspace' : 'personal');
    const workspace = workspaces.find((row) => (
      row.scope === preferredScope && Number(row.id) === preferredWorkspace
    )) || workspaces[0] || null;
    const preferredVault = Number(
      (pendingMatchesConnection ? pending?.vaultId : 0)
      || connection?.defaultVaultId
      || defaults?.defaultVaultId
      || 0
    );
    const vault = workspace?.vaults?.find((row) => Number(row.id) === preferredVault)
      || workspace?.vaults?.find((row) => row.isDefault)
      || workspace?.vaults?.[0]
      || null;
    return { connection, workspace, vault };
  }

  function showSavePrompt(pending, destinationData = {}) {
    if (!pending?.secret || state.saveHost?.isConnected) {
      return;
    }
    const connections = Array.isArray(destinationData.connections)
      ? destinationData.connections
      : [{
        connectionId: destinationData.connectionId || '',
        connectionName: destinationData.tenant?.name || 'OrbitOne',
        tenant: destinationData.tenant || null,
        workspaces: destinationData.workspaces || [],
        defaultScope: destinationData.defaultScope,
        defaultWorkspaceId: destinationData.defaultWorkspaceId,
        defaultVaultId: destinationData.defaultVaultId,
      }];
    if (!connections.some((connection) => connection.workspaces?.length)) {
      return;
    }
    const selected = selectDestination(connections, pending, destinationData);
    const host = document.createElement('div');
    host.id = 'orbitone-vault-save-prompt';
    host.style.cssText = 'all:initial;position:fixed;inset:0;z-index:2147483647;pointer-events:none;';
    const shadow = host.attachShadow({ mode: 'closed' });
    const iconUrl = browserApi.runtime.getURL('icons/icon-32.png');

    shadow.innerHTML = `
      <style>
        :host { all: initial; }
        * { box-sizing: border-box; letter-spacing: 0; }
        .ov-save {
          position: fixed; right: 18px; bottom: 18px; width: min(390px, calc(100vw - 24px));
          max-height: calc(100vh - 36px); overflow: auto; padding: 18px;
          color: #3f4b5e; background: #fff; border: 1px solid #dfe5ec; border-radius: 8px;
          box-shadow: 0 18px 48px rgba(21,39,64,.2); pointer-events: auto;
          font: 13px/1.45 Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
        }
        .ov-head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
        .ov-head img { width: 28px; height: 28px; }
        .ov-head div { min-width: 0; }
        .ov-head strong { display: block; color: #162033; font-size: 15px; }
        .ov-head span { display: block; overflow: hidden; color: #5f6b7d; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
        .ov-close {
          width: 32px; height: 32px; margin-left: auto; padding: 0; color: #5f6b7d; background: #fff;
          border: 1px solid #dfe5ec; border-radius: 6px; cursor: pointer; font-size: 20px; line-height: 1;
        }
        .ov-form { display: grid; gap: 10px; }
        .ov-label { display: grid; gap: 5px; color: #3f4b5e; font-size: 11px; font-weight: 700; }
        .ov-input, .ov-select {
          width: 100%; min-height: 40px; padding: 8px 10px; color: #162033; background: #fff;
          border: 1px solid #cdd6e0; border-radius: 6px; font: inherit; font-size: 13px; line-height: 1.3;
        }
        .ov-input:focus, .ov-select:focus { border-color: #0875e1; outline: 2px solid rgba(8,117,225,.15); }
        .ov-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
        .ov-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 3px; }
        .ov-button {
          min-height: 40px; padding: 0 14px; border: 1px solid #cdd6e0; border-radius: 6px;
          background: #fff; color: #162033; cursor: pointer; font: inherit; font-size: 13px; font-weight: 700; line-height: 1.2;
        }
        .ov-button-primary { color: #fff; background: #0875e1; border-color: #0875e1; }
        .ov-button:disabled { cursor: wait; opacity: .65; }
        .ov-status { min-height: 18px; color: #5f6b7d; font-size: 11px; }
        .ov-status.error { color: #b42318; }
        @media (max-width: 430px) {
          .ov-save { right: 12px; bottom: 12px; }
          .ov-grid { grid-template-columns: 1fr; }
        }
      </style>
      <section class="ov-save" role="dialog" aria-modal="false" aria-labelledby="ov-save-title">
        <div class="ov-head">
          <img src="${iconUrl}" alt="">
          <div>
            <strong id="ov-save-title">${escapeHtml(pending.mode === 'update' ? t('updatePassword', undefined, 'Passwort aktualisieren') : t('saveCredentials', undefined, 'Zugangsdaten speichern'))}</strong>
            <span>${escapeHtml(new URL(pending.url).hostname)}</span>
          </div>
          <button class="ov-close" type="button" aria-label="${escapeHtml(t('close', undefined, 'Schließen'))}" title="${escapeHtml(t('close', undefined, 'Schließen'))}">×</button>
        </div>
        <form class="ov-form">
          <label class="ov-label">${escapeHtml(t('title', undefined, 'Titel'))}
            <input class="ov-input" name="title" value="${escapeHtml(pending.title)}" required>
          </label>
          <label class="ov-label">${escapeHtml(t('username', undefined, 'Benutzername'))}
            <input class="ov-input" name="username" value="${escapeHtml(pending.username)}" autocomplete="off">
          </label>
          <label class="ov-label">${escapeHtml(t('tenant', undefined, 'Mandant'))}
            <select class="ov-select" name="connectionId" required>
              ${connections.map((connection) => `
                <option value="${escapeHtml(connection.connectionId)}" ${connection.connectionId === selected.connection?.connectionId ? 'selected' : ''}>${escapeHtml(connection.tenant?.name || connection.connectionName)}</option>
              `).join('')}
            </select>
          </label>
          <div class="ov-grid">
            <label class="ov-label">${escapeHtml(t('location', undefined, 'Speicherort'))}
              <select class="ov-select" name="workspaceId" required></select>
            </label>
            <label class="ov-label">${escapeHtml(t('subfolder', undefined, 'Unterordner'))}
              <select class="ov-select" name="vaultId" required></select>
            </label>
          </div>
          <input name="secret" type="hidden" value="${escapeHtml(pending.secret)}">
          <div class="ov-status" aria-live="polite"></div>
          <div class="ov-actions">
            <button class="ov-button ov-ignore" type="button">${escapeHtml(t('ignoreCredential', undefined, 'Diesen Zugang ignorieren'))}</button>
            <button class="ov-button ov-cancel" type="button">${escapeHtml(t('notNow', undefined, 'Nicht jetzt'))}</button>
            <button class="ov-button ov-button-primary" type="submit">${escapeHtml(pending.mode === 'update' ? t('update', undefined, 'Aktualisieren') : t('save', undefined, 'Speichern'))}</button>
          </div>
        </form>
      </section>
    `;

    const form = shadow.querySelector('.ov-form');
    const connectionSelect = form?.elements?.connectionId;
    const workspaceSelect = form?.elements?.workspaceId;
    const vaultSelect = form?.elements?.vaultId;
    const status = shadow.querySelector('.ov-status');
    const submit = form?.querySelector('[type="submit"]');

    const selectedConnection = () => connections.find((row) => row.connectionId === connectionSelect?.value);

    const renderVaults = (preferredVaultId = 0) => {
      const workspaces = Array.isArray(selectedConnection()?.workspaces)
        ? selectedConnection().workspaces
        : [];
      const workspace = workspaces.find((row) => Number(row.id) === Number(workspaceSelect?.value || 0));
      const vaults = Array.isArray(workspace?.vaults) ? workspace.vaults : [];
      const selectedVault = vaults.find((row) => Number(row.id) === Number(preferredVaultId))
        || vaults.find((row) => row.isDefault)
        || vaults[0];
      if (vaultSelect) {
        vaultSelect.innerHTML = vaults.map((vault) => `
          <option value="${Number(vault.id)}" ${vault.id === selectedVault?.id ? 'selected' : ''}>${escapeHtml(vault.name)}</option>
        `).join('');
      }
    };

    const renderWorkspaces = (preferredWorkspaceId = 0, preferredVaultId = 0) => {
      const connection = selectedConnection();
      const workspaces = Array.isArray(connection?.workspaces) ? connection.workspaces : [];
      const preferredScope = pending?.connectionId === connection?.connectionId
        ? pending?.scope
        : connection?.defaultScope;
      const selectedWorkspace = workspaces.find((row) => (
        Number(row.id) === Number(preferredWorkspaceId)
        && (!preferredScope || row.scope === preferredScope)
      )) || workspaces[0];
      workspaceSelect.innerHTML = workspaces.map((workspace) => `
        <option value="${Number(workspace.id)}" ${workspace === selectedWorkspace ? 'selected' : ''}>${escapeHtml(workspace.name)}</option>
      `).join('');
      renderVaults(preferredVaultId);
    };

    renderWorkspaces(selected.workspace?.id || 0, selected.vault?.id || 0);
    connectionSelect?.addEventListener('change', () => {
      const connection = selectedConnection();
      renderWorkspaces(connection?.defaultWorkspaceId || 0, connection?.defaultVaultId || 0);
    });
    workspaceSelect?.addEventListener('change', () => renderVaults());
    shadow.querySelector('.ov-close')?.addEventListener('click', () => removeSavePrompt(true));
    shadow.querySelector('.ov-cancel')?.addEventListener('click', () => removeSavePrompt(true));
    shadow.querySelector('.ov-ignore')?.addEventListener('click', async () => {
      const response = await sendMessage({
        type: 'orbitone:ignore-credential-host',
        url: pending.url,
      }).catch((error) => ({ ok: false, error: { message: error.message } }));
      if (!response?.ok) {
        status.textContent = response?.error?.message || t('errorIgnoreCredential', undefined, 'Die Website konnte nicht ignoriert werden.');
        status.classList.add('error');
        return;
      }
      removeSavePrompt(false);
    });
    form?.addEventListener('submit', async (event) => {
      event.preventDefault();
      status.textContent = t('savingEncrypted', undefined, 'Wird verschlüsselt gespeichert...');
      status.classList.remove('error');
      submit.disabled = true;
      const data = new FormData(form);
      const selectedConnectionRow = connections.find((row) => row.connectionId === String(data.get('connectionId') || ''));
      const workspaces = Array.isArray(selectedConnectionRow?.workspaces) ? selectedConnectionRow.workspaces : [];
      const selectedWorkspace = workspaces.find((row) => Number(row.id) === Number(data.get('workspaceId') || 0));
      const response = await sendMessage({
        type: 'orbitone:save-credential',
        credential: {
          entryId: pending.connectionId === String(data.get('connectionId') || '')
            ? Number(pending.entryId || 0)
            : 0,
          connectionId: String(data.get('connectionId') || ''),
          scope: selectedWorkspace?.scope || 'personal',
          workspaceId: Number(data.get('workspaceId') || 0),
          vaultId: Number(data.get('vaultId') || 0),
          title: String(data.get('title') || ''),
          username: String(data.get('username') || ''),
          secret: String(data.get('secret') || pending.secret || ''),
          url: pending.url,
          notes: pending.notes || '',
        },
      }).catch((error) => ({ ok: false, error: { message: error.message } }));

      if (!response?.ok) {
        status.textContent = response?.error?.message || t('errorSaveCredentials', undefined, 'Zugangsdaten konnten nicht gespeichert werden.');
        status.classList.add('error');
        submit.disabled = false;
        return;
      }
      status.textContent = t('savedInVault', undefined, 'Im OrbitOne Vault gespeichert.');
      window.setTimeout(() => removeSavePrompt(false), 900);
    });

    (document.body || document.documentElement).appendChild(host);
    state.saveHost = host;
  }

  function capturedCredential(root) {
    const passwordField = bestPasswordField(root, 'capture');
    if (!passwordField?.value) {
      return null;
    }
    const scope = passwordField.form || root || document;
    const usernameField = bestUsernameField(scope, passwordField);
    const recentUsername = Date.now() - state.lastUsernameAt < 2 * 60 * 1000
      ? state.lastUsername
      : '';
    return {
      title: document.title || location.hostname,
      username: String(usernameField?.value || recentUsername || '').trim(),
      secret: String(passwordField.value || ''),
      url: location.href,
    };
  }

  function rememberCredential(root = document) {
    const credential = capturedCredential(root);
    if (!credential) {
      return null;
    }
    state.lastCapturedCredential = credential;
    state.lastCapturedAt = Date.now();
    return credential;
  }

  function recentCredential() {
    if (
      !state.lastCapturedCredential
      || Date.now() - state.lastCapturedAt > 2 * 60 * 1000
      || new URL(state.lastCapturedCredential.url).hostname !== location.hostname
    ) {
      return null;
    }
    return { ...state.lastCapturedCredential, url: location.href };
  }

  function hasDestinations(data) {
    if (Array.isArray(data?.connections)) {
      return data.connections.some((connection) => Array.isArray(connection?.workspaces) && connection.workspaces.length);
    }
    return Array.isArray(data?.workspaces) && data.workspaces.length > 0;
  }

  async function reportCredential(root) {
    const credential = rememberCredential(root) || recentCredential();
    if (!credential) {
      return;
    }
    const signature = `${location.hostname}|${credential.username}|${credential.secret}`;
    if (signature === state.lastDetectionSignature && Date.now() - state.lastDetectionAt < 4000) {
      return;
    }
    state.lastDetectionSignature = signature;
    state.lastDetectionAt = Date.now();

    let response = null;
    try {
      response = await sendMessage({
        type: 'orbitone:credentials-detected',
        credential,
      });
    } catch (_error) {
      if (state.lastDetectionSignature === signature) {
        state.lastDetectionSignature = '';
        state.lastDetectionAt = 0;
      }
      return;
    }
    if (response?.ok && response.prompt) {
      let destinationData = response.destinations || {};
      if (!hasDestinations(destinationData)) {
        const destinationsResponse = await sendMessage({ type: 'orbitone:get-destinations' }).catch(() => null);
        if (destinationsResponse?.ok) {
          destinationData = destinationsResponse;
        }
      }
      showSavePrompt(response.prompt, destinationData);
    }
  }

  async function notifyBackground() {
    const hasPassword = passwordFields(document).length > 0;
    const response = await sendMessage({
      type: 'orbitone:content-ready',
      url: location.href,
      hasPassword,
    }).catch(() => null);
    if (!response?.ok || response.configured === false) {
      removeLauncher();
      return;
    }

    state.matches = Array.isArray(response.matches) ? response.matches : [];
    renderLauncher(state.matches);
    if (
      response.autofill?.secret
      && String(response.autofill.entryKey || response.autofill.entryId || '') !== state.autoFilledEntryKey
    ) {
      const result = fillCredentials(response.autofill, { onlyEmpty: true });
      if (result.ok) {
        state.autoFilledEntryKey = String(response.autofill.entryKey || response.autofill.entryId || '');
      }
    }
    if (response.pendingCredential && response.savePromptEnabled !== false) {
      const destinationsResponse = await sendMessage({ type: 'orbitone:get-destinations' }).catch(() => null);
      if (destinationsResponse?.ok) {
        showSavePrompt(response.pendingCredential, destinationsResponse);
      }
    }
  }

  function scheduleNotify() {
    window.clearTimeout(state.notifyTimer);
    state.notifyTimer = window.setTimeout(() => notifyBackground().catch(() => null), 180);
  }

  document.addEventListener('input', (event) => {
    const input = event.target instanceof HTMLInputElement ? event.target : null;
    if (!input) {
      return;
    }
    const type = String(input.type || 'text').toLowerCase();
    const signature = inputSignature(input);
    if (
      ['text', 'email', 'tel'].includes(type)
      && (
        type === 'email'
        || String(input.autocomplete || '').toLowerCase() === 'username'
        || /(user|login|konto|account|mail|email|e-mail|benutzer)/.test(signature)
      )
    ) {
      state.lastUsername = String(input.value || '').trim();
      state.lastUsernameAt = Date.now();
    }
    if (type === 'password' || passwordFields(input.form || document).length > 0) {
      rememberCredential(input.form || document);
    }
  }, true);

  document.addEventListener('submit', (event) => {
    const root = event.target instanceof HTMLFormElement ? event.target : document;
    rememberCredential(root);
    reportCredential(root).catch(() => null);
  }, true);

  function reportFromAction(event) {
    const button = event.target instanceof Element
      ? event.target.closest('button, input[type="submit"], [role="button"]')
      : null;
    if (!button) {
      return;
    }
    const form = button instanceof HTMLButtonElement || button instanceof HTMLInputElement
      ? button.form
      : button.closest('form');
    if (form && (bestPasswordField(form, 'capture') || recentCredential())) {
      rememberCredential(form);
      reportCredential(form).catch(() => null);
      return;
    }
    const signature = [
      button.textContent,
      button.getAttribute('aria-label'),
      button.getAttribute('name'),
      button.getAttribute('id'),
      button.getAttribute('type'),
    ].join(' ').toLowerCase();
    if (/(login|log in|sign in|signin|anmelden|einloggen|weiter|continue|submit|speichern|registr)/.test(signature)) {
      reportCredential(button.closest('form') || document).catch(() => null);
    }
  }

  document.addEventListener('pointerdown', reportFromAction, true);
  document.addEventListener('click', reportFromAction, true);

  document.addEventListener('keydown', (event) => {
    if (event.key !== 'Enter') {
      return;
    }
    const target = event.target instanceof Element ? event.target : null;
    const root = target?.closest('form') || document;
    if (bestPasswordField(root, 'capture') || recentCredential()) {
      rememberCredential(root);
      reportCredential(root).catch(() => null);
    }
  }, true);

  const observer = new MutationObserver((mutations) => {
    if (mutations.some((mutation) => Array.from(mutation.addedNodes).some((node) => (
      node instanceof Element
      && (node.matches('input[type="password"]') || node.querySelector('input[type="password"]'))
    )))) {
      scheduleNotify();
    }
  });
  observer.observe(document.documentElement, { childList: true, subtree: true });

  browserApi.runtime.onMessage.addListener((message, _sender, sendResponse) => {
    if (message?.type !== 'orbitone-fill-credentials') {
      return false;
    }
    sendResponse(fillCredentials(message?.payload || {}));
    return true;
  });

  scheduleNotify();
}());
