(function backgroundController() {
  'use strict';

  if (!globalThis.OrbitOneVaultExtension && typeof importScripts === 'function') {
    importScripts('extension-common.js');
  }

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

  const MATCH_CACHE_TTL = 5 * 60 * 1000;
  const DETAIL_CACHE_TTL = 2 * 60 * 1000;
  const CONTEXT_CACHE_TTL = 5 * 60 * 1000;
  const PENDING_TTL = 3 * 60 * 1000;
  const UPDATE_ALARM = 'orbitone-vault-update-check';
  const UPDATE_INTERVAL_MINUTES = 6 * 60;
  const UPDATE_STATE_KEY = 'orbitoneUpdateState';
  const matchCache = new Map();
  const detailCache = new Map();
  const contextCache = new Map();
  let pendingCredentialMemory = null;

  function now() {
    return Date.now();
  }

  function withoutSecrets(entry) {
    if (!entry || typeof entry !== 'object') {
      return null;
    }
    const { secret: _secret, password: _password, ...safe } = entry;
    return safe;
  }

  function errorPayload(error) {
    return {
      ok: false,
      error: {
        code: String(error?.code || 'extension_error'),
        message: String(error?.message || 'OrbitOne Vault konnte die Anfrage nicht ausführen.'),
      },
    };
  }

  function clearCaches() {
    matchCache.clear();
    detailCache.clear();
    contextCache.clear();
  }

  function currentVersion() {
    return String(browserApi.runtime.getManifest?.().version || '0.0.0');
  }

  function compareVersions(left, right) {
    const leftParts = String(left || '').split(/[.-]/).map((value) => Number.parseInt(value, 10) || 0);
    const rightParts = String(right || '').split(/[.-]/).map((value) => Number.parseInt(value, 10) || 0);
    const length = Math.max(leftParts.length, rightParts.length);
    for (let index = 0; index < length; index += 1) {
      const difference = (leftParts[index] || 0) - (rightParts[index] || 0);
      if (difference !== 0) {
        return difference;
      }
    }
    return 0;
  }

  function browserChannel() {
    const userAgent = String(globalThis.navigator?.userAgent || '');
    if (/Edg\//i.test(userAgent)) return 'edge';
    if (/Firefox\//i.test(userAgent)) return 'firefox';
    if (/Safari\//i.test(userAgent) && !/Chrom(?:e|ium)\//i.test(userAgent)) return 'safari';
    return 'chrome';
  }

  async function requestNativeUpdateCheck() {
    const runtime = browserApi.runtime;
    if (typeof runtime.requestUpdateCheck !== 'function') {
      return { status: 'not_supported' };
    }
    if (globalThis.browser) {
      return runtime.requestUpdateCheck();
    }
    return new Promise((resolve, reject) => {
      runtime.requestUpdateCheck((status, details = {}) => {
        const lastError = globalThis.chrome?.runtime?.lastError;
        if (lastError) {
          reject(new Error(lastError.message || 'Native Updateprüfung fehlgeschlagen.'));
          return;
        }
        if (status && typeof status === 'object') {
          resolve(status);
          return;
        }
        resolve({ status: String(status || 'no_update'), version: String(details?.version || '') });
      });
    });
  }

  async function setUpdateBadge(updateAvailable) {
    if (!browserApi.action?.setBadgeText) {
      return;
    }
    await Promise.resolve(browserApi.action.setBadgeText({ text: updateAvailable ? '!' : '' })).catch(() => null);
    if (updateAvailable && browserApi.action?.setBadgeBackgroundColor) {
      await Promise.resolve(browserApi.action.setBadgeBackgroundColor({ color: '#0875e1' })).catch(() => null);
    }
  }

  async function loadReleaseFeed() {
    const response = await fetch(ext.RELEASE_FEED_URL, {
      headers: { Accept: 'application/json' },
      cache: 'no-store',
      credentials: 'omit',
    });
    if (!response.ok) {
      throw new Error(`Release-Feed nicht erreichbar (${response.status}).`);
    }
    const payload = await response.json();
    if (!payload || typeof payload !== 'object' || !payload.version) {
      throw new Error('Release-Feed ist ungültig.');
    }
    return payload;
  }

  async function checkForUpdates(manual = false) {
    const checkedAt = new Date().toISOString();
    let nativeResult = { status: 'not_supported' };
    try {
      nativeResult = await requestNativeUpdateCheck();
    } catch (error) {
      nativeResult = { status: 'failed', message: String(error?.message || '') };
    }

    try {
      const release = await loadReleaseFeed();
      const installedVersion = currentVersion();
      const latestVersion = String(release.version || installedVersion);
      const updateAvailable = compareVersions(latestVersion, installedVersion) > 0;
      const download = release?.downloads?.[browserChannel()] || null;
      const state = {
        checkedAt,
        installedVersion,
        latestVersion,
        updateAvailable,
        nativeStatus: String(nativeResult?.status || 'unknown'),
        downloadUrl: String(download?.url || ''),
        manual,
      };
      await ext.extension.storage.set({ [UPDATE_STATE_KEY]: state });
      await setUpdateBadge(updateAvailable);
      return state;
    } catch (error) {
      const state = {
        checkedAt,
        installedVersion: currentVersion(),
        latestVersion: '',
        updateAvailable: false,
        nativeStatus: String(nativeResult?.status || 'unknown'),
        error: String(error?.message || 'Updateprüfung fehlgeschlagen.'),
        manual,
      };
      await ext.extension.storage.set({ [UPDATE_STATE_KEY]: state });
      return state;
    }
  }

  async function getUpdateState() {
    const stored = await ext.extension.storage.get({ [UPDATE_STATE_KEY]: null });
    return stored?.[UPDATE_STATE_KEY] || {
      installedVersion: currentVersion(),
      latestVersion: '',
      updateAvailable: false,
      checkedAt: '',
    };
  }

  async function scheduleUpdateChecks() {
    if (!browserApi.alarms?.create) {
      return;
    }
    await Promise.resolve(browserApi.alarms.create(UPDATE_ALARM, {
      delayInMinutes: 2,
      periodInMinutes: UPDATE_INTERVAL_MINUTES,
    })).catch(() => null);
  }

  function cleanPageUrl(rawUrl) {
    const parsed = ext.parseWebUrl(rawUrl);
    if (!parsed) {
      return '';
    }
    parsed.hash = '';
    parsed.search = '';
    return parsed.toString();
  }

  async function settingsOrThrow() {
    const settings = await ext.getSettings();
    if (!ext.isConfigured(settings)) {
      const error = new Error('OrbitOne Vault ist noch nicht verbunden.');
      error.code = 'not_configured';
      throw error;
    }
    return settings;
  }

  async function connectionOrThrow(connectionId = '') {
    const settings = await settingsOrThrow();
    const configured = ext.configuredConnections(settings);
    const requested = ext.splitTenantConnectionId(connectionId);
    const connection = requested.connectionId
      ? configured.find((row) => row.id === requested.connectionId)
      : (configured.find((row) => row.id === settings.activeConnectionId) || configured[0]);
    if (!connection?.baseUrl || !connection?.apiToken) {
      const error = new Error('Die ausgewählte OrbitOne-Verbindung ist nicht eingerichtet.');
      error.code = 'connection_not_configured';
      throw error;
    }
    return {
      settings,
      connection,
      tenantId: Number(requested.tenantId || 0),
    };
  }

  async function loadContext(force = false, connectionId = '', tenantId = 0) {
    const { connection } = await connectionOrThrow(connectionId);
    const organizationId = Number(tenantId || 0);
    const key = `${connection.id}|${connection.baseUrl}|${connection.apiToken.slice(-8)}|${organizationId || 'current'}`;
    const cached = contextCache.get(key);
    if (!force && cached && now() - cached.loadedAt < CONTEXT_CACHE_TTL) {
      return cached.value;
    }
    const value = await ext.loadExtensionContext(connection.baseUrl, connection.apiToken, organizationId);
    contextCache.set(key, { loadedAt: now(), value });
    return value;
  }

  async function loadConnectionContexts(connection, force = false) {
    const initial = await loadContext(force, connection.id);
    const currentTenantId = Number(initial?.tenant?.id || 0);
    const organizations = [
      ...(Array.isArray(initial?.organizations) ? initial.organizations : []),
      initial?.tenant || null,
    ].filter((organization) => Number(organization?.id || 0) > 0);
    const uniqueOrganizations = [...new Map(
      organizations.map((organization) => [Number(organization.id), organization])
    ).values()];
    if (!uniqueOrganizations.length) {
      return [initial];
    }

    const settled = await Promise.allSettled(uniqueOrganizations.map((organization) => (
      Number(organization.id) === currentTenantId
        ? Promise.resolve(initial)
        : loadContext(force, connection.id, Number(organization.id))
    )));
    const contexts = settled
      .filter((result) => result.status === 'fulfilled')
      .map((result) => result.value);
    if (!contexts.length) {
      const firstError = settled.find((result) => result.status === 'rejected');
      throw firstError?.reason || new Error('Die Mandanten konnten nicht geladen werden.');
    }
    return contexts;
  }

  function writableDestinations(context) {
    const locations = [];
    if (context?.personal && Array.isArray(context.personal.vaults)) {
      locations.push({
        id: 0,
        scope: 'personal',
        name: String(context.personal.name || 'Persönlicher Vault'),
        canWrite: context.personal.canWrite === true,
        vaults: context.personal.vaults
          .filter((vault) => vault.canWrite === true)
          .map((vault) => ({
            id: Number(vault.id || 0),
            workspaceId: 0,
            scope: 'personal',
            personal: true,
            name: String(vault.name || ''),
            isDefault: vault.isDefault === true,
            entryCount: Number(vault.entryCount || 0),
            canWrite: true,
          })),
      });
    }

    const workspaces = (Array.isArray(context?.workspaces) ? context.workspaces : [])
      .map((workspace) => ({
        id: Number(workspace.id || 0),
        scope: 'workspace',
        name: String(workspace.name || ''),
        canWrite: workspace.canWrite === true,
        vaults: (Array.isArray(workspace.vaults) ? workspace.vaults : [])
          .filter((vault) => vault.canWrite === true)
          .map((vault) => ({
            id: Number(vault.id || 0),
            workspaceId: Number(workspace.id || 0),
            scope: 'workspace',
            personal: false,
            name: String(vault.name || ''),
            isDefault: vault.isDefault === true,
            entryCount: Number(vault.entryCount || 0),
            canWrite: true,
          })),
      }))
      .filter((workspace) => workspace.vaults.length > 0);
    return [...locations.filter((location) => location.vaults.length > 0), ...workspaces];
  }

  async function connectionDestinations(connection, force = false) {
    const contexts = await loadConnectionContexts(connection, force);
    return contexts.map((context) => {
      const tenantId = Number(context?.tenant?.id || 0);
      const defaults = ext.tenantDefaults(connection, tenantId);
      return {
        connectionId: ext.tenantConnectionId(connection.id, tenantId),
        authConnectionId: connection.id,
        contextId: ext.tenantConnectionId(connection.id, tenantId),
        connectionName: connection.name,
        tenantId,
        user: context?.user || null,
        tenant: context?.tenant || null,
        workspaces: writableDestinations(context),
        defaultScope: defaults.scope,
        defaultWorkspaceId: defaults.workspaceId,
        defaultVaultId: defaults.vaultId,
      };
    });
  }

  async function destinations(force = false, requestedConnectionId = '', includeAll = true) {
    const settings = await settingsOrThrow();
    const configured = ext.configuredConnections(settings);
    const selected = requestedConnectionId
      ? configured.filter((connection) => connection.id === requestedConnectionId)
      : (includeAll ? configured : [ext.activeConnection(settings)].filter(Boolean));
    const settled = await Promise.allSettled(
      selected.map((connection) => connectionDestinations(connection, force))
    );
    const connections = settled
      .filter((result) => result.status === 'fulfilled')
      .flatMap((result) => result.value);
    if (!connections.length) {
      const firstError = settled.find((result) => result.status === 'rejected');
      throw firstError?.reason || new Error('Keine OrbitOne-Verbindung konnte geladen werden.');
    }
    const active = connections.find((connection) => (
      connection.authConnectionId === settings.activeConnectionId
      && Number(connection.tenantId) === Number(settings.activeTenantId || 0)
    ))
      || connections[0];
    return {
      connections,
      activeConnectionId: active?.connectionId || '',
      activeTenantId: Number(active?.tenantId || 0),
      connectionErrors: settled
        .map((result, index) => result.status === 'rejected' ? ({
          connectionId: selected[index]?.id || '',
          connectionName: selected[index]?.name || '',
          message: String(result.reason?.message || 'Verbindung fehlgeschlagen.'),
        }) : null)
        .filter(Boolean),
      ...(active || {}),
    };
  }

  async function tenantTargets(settings, requestedConnectionId = '', force = false) {
    const requested = ext.splitTenantConnectionId(requestedConnectionId);
    const connections = ext.configuredConnections(settings)
      .filter((connection) => !requested.connectionId || connection.id === requested.connectionId);
    const settled = await Promise.allSettled(connections.map(async (connection) => {
      const contexts = await loadConnectionContexts(connection, force);
      return contexts
        .filter((context) => !requested.tenantId || Number(context?.tenant?.id || 0) === requested.tenantId)
        .map((context) => ({
          connection,
          context,
          tenantId: Number(context?.tenant?.id || 0),
        }));
    }));
    const targets = settled.flatMap((result) => result.status === 'fulfilled' ? result.value : []);
    if (!targets.length && settled.length && settled.every((result) => result.status === 'rejected')) {
      throw settled[0].reason;
    }
    return targets;
  }

  async function getMatchesForTenant(connection, context, pageUrl, settings, options = {}) {
    const host = ext.pageHost(pageUrl);
    if (!host) {
      return [];
    }

    const tenantId = Number(context?.tenant?.id || 0);
    const contextId = ext.tenantConnectionId(connection.id, tenantId);
    const key = `${contextId}|${connection.baseUrl}|${host}|${settings.matchSubdomains ? 'sub' : 'exact'}`;
    const cached = matchCache.get(key);
    if (!options.force && cached && now() - cached.loadedAt < MATCH_CACHE_TTL) {
      return cached.rows;
    }

    const rows = await ext.searchVaultEntries(connection.baseUrl, connection.apiToken, host, '', tenantId);
    const matches = rows
      .map((entry) => ({
        ...withoutSecrets(entry),
        connectionId: contextId,
        authConnectionId: connection.id,
        tenantId,
        connectionName: connection.name,
        tenantName: context?.tenant?.name || connection.tenantName || connection.name,
        exactDomain: ext.isExactDomainMatch(entry, pageUrl),
        matchScore: ext.scoreEntryForUrl(entry, pageUrl, settings.matchSubdomains),
      }))
      .filter((entry) => entry.matchScore > 0)
      .sort((left, right) => {
        if (right.matchScore !== left.matchScore) {
          return right.matchScore - left.matchScore;
        }
        return String(right.updatedAt || '').localeCompare(String(left.updatedAt || ''));
      })
      .slice(0, 25);

    matchCache.set(key, { loadedAt: now(), rows: matches });
    return matches;
  }

  async function getMatches(pageUrl, options = {}) {
    const settings = await settingsOrThrow();
    const targets = await tenantTargets(settings, options.connectionId, options.force === true);
    const settled = await Promise.allSettled(
      targets.map(({ connection, context }) => getMatchesForTenant(connection, context, pageUrl, settings, options))
    );
    const matches = settled.flatMap((result) => result.status === 'fulfilled' ? result.value : []);
    if (!matches.length && settled.length && settled.every((result) => result.status === 'rejected')) {
      throw settled[0].reason;
    }
    return matches
      .sort((left, right) => {
        if (right.matchScore !== left.matchScore) {
          return right.matchScore - left.matchScore;
        }
        return String(right.updatedAt || '').localeCompare(String(left.updatedAt || ''));
      })
      .slice(0, 100);
  }

  async function searchEntries(query, connectionId = '') {
    const settings = await settingsOrThrow();
    const targets = await tenantTargets(settings, connectionId);
    const settled = await Promise.allSettled(targets.map(async ({ connection, context, tenantId }) => {
      const rows = await ext.searchVaultEntries(
        connection.baseUrl,
        connection.apiToken,
        String(query || '').trim(),
        '',
        tenantId
      );
      const contextId = ext.tenantConnectionId(connection.id, tenantId);
      return rows.map((entry) => ({
        ...withoutSecrets(entry),
        connectionId: contextId,
        authConnectionId: connection.id,
        tenantId,
        connectionName: connection.name,
        tenantName: context?.tenant?.name || connection.tenantName || connection.name,
      })).filter(Boolean);
    }));
    const rows = settled.flatMap((result) => result.status === 'fulfilled' ? result.value : []);
    if (!rows.length && settled.length && settled.every((result) => result.status === 'rejected')) {
      throw settled[0].reason;
    }
    return rows.slice(0, 100);
  }

  async function entryConnectionOrThrow(entry) {
    return connectionOrThrow(String(entry?.connectionId || ''));
  }

  async function getEntryDetail(entry, force = false) {
    const { connection } = await entryConnectionOrThrow(entry);
    const key = [
      connection.id,
      entry?.tenantId || entry?.tenant_id || 0,
      entry?.scope || (entry?.personal ? 'personal' : 'workspace'),
      entry?.workspaceId || entry?.workspace_id || 0,
      entry?.vaultId || entry?.vault_id || 0,
      entry?.id || 0,
    ].join(':');
    const cached = detailCache.get(key);
    if (!force && cached && now() - cached.loadedAt < DETAIL_CACHE_TTL) {
      return cached.value;
    }
    const value = await ext.loadVaultEntryDetail(connection.baseUrl, connection.apiToken, entry);
    detailCache.set(key, { loadedAt: now(), value });
    return value;
  }

  async function getPasswordHistory(entry) {
    const { connection } = await entryConnectionOrThrow(entry);
    return ext.loadPasswordHistory(connection.baseUrl, connection.apiToken, entry);
  }

  async function setPendingCredential(value) {
    pendingCredentialMemory = value || null;
    if (ext.extension.storage.session.available) {
      if (value) {
        await ext.extension.storage.session.set({ orbitonePendingCredential: value });
      } else {
        await ext.extension.storage.session.remove('orbitonePendingCredential');
      }
    }
  }

  async function getPendingCredential(pageUrl = '') {
    let pending = pendingCredentialMemory;
    if (!pending && ext.extension.storage.session.available) {
      const stored = await ext.extension.storage.session.get({ orbitonePendingCredential: null });
      pending = stored?.orbitonePendingCredential || null;
      pendingCredentialMemory = pending;
    }
    if (!pending) {
      return null;
    }
    if (now() - Number(pending.detectedAt || 0) > PENDING_TTL) {
      await setPendingCredential(null);
      return null;
    }
    if (pageUrl && ext.pageHost(pageUrl) !== ext.pageHost(pending.url)) {
      return null;
    }
    return pending;
  }

  function createPendingCredential(payload) {
    const url = cleanPageUrl(payload?.url);
    const host = ext.pageHost(url);
    if (!url || !host) {
      throw new Error('Die Website-Adresse konnte nicht erkannt werden.');
    }
    const secret = String(payload?.secret || '');
    if (!secret) {
      throw new Error('Es wurde kein Passwort erkannt.');
    }
    return {
      pendingId: `${now()}-${Math.random().toString(36).slice(2, 10)}`,
      detectedAt: now(),
      title: String(payload?.title || host).trim() || host,
      username: String(payload?.username || '').trim(),
      secret,
      url,
      notes: '',
      entryId: 0,
      connectionId: '',
      tenantId: 0,
      workspaceId: 0,
      vaultId: 0,
      scope: '',
      mode: 'create',
    };
  }

  async function detectCredential(payload) {
    const settings = await settingsOrThrow();
    if (settings.savePromptEnabled === false) {
      return { prompt: null };
    }
    const detectedHost = ext.pageHost(payload?.url);
    if (detectedHost && (settings.ignoredCredentialHosts || []).includes(detectedHost)) {
      await setPendingCredential(null);
      return { prompt: null, ignored: true };
    }

    const pending = createPendingCredential(payload);
    const matches = await getMatches(pending.url).catch(() => []);
    const sameAccounts = matches.filter((entry) => (
      entry.exactDomain
      && String(entry.username || '').trim().toLowerCase() === pending.username.toLowerCase()
    ));

    const accountDetails = await Promise.all(sameAccounts.map(async (entry) => ({
      entry,
      detail: await getEntryDetail(entry).catch(() => null),
    })));
    if (accountDetails.some(({ detail }) => detail?.secret === pending.secret)) {
      return { prompt: null, unchanged: true };
    }

    if (accountDetails.length === 1) {
      const { entry: sameAccount, detail } = accountDetails[0];
      if (detail) {
        pending.mode = 'update';
        pending.entryId = Number(sameAccount.id || 0);
        pending.connectionId = String(sameAccount.connectionId || '');
        pending.tenantId = Number(sameAccount.tenantId || sameAccount.tenant_id || 0);
        pending.workspaceId = Number(sameAccount.workspaceId || sameAccount.workspace_id || 0);
        pending.vaultId = Number(sameAccount.vaultId || sameAccount.vault_id || 0);
        pending.scope = sameAccount.scope === 'personal' || sameAccount.personal === true ? 'personal' : 'workspace';
        pending.title = String(sameAccount.title || pending.title);
        pending.notes = String(detail.notes || '');
      }
    }

    await setPendingCredential(pending);
    return {
      prompt: pending,
      destinations: await destinations().catch(() => ({ connections: [], workspaces: [] })),
    };
  }

  async function ignoreCredentialHost(url) {
    const host = ext.pageHost(url);
    if (!host) {
      throw new Error('Die Website-Adresse konnte nicht erkannt werden.');
    }
    const settings = await settingsOrThrow();
    await ext.saveSettings({
      ...settings,
      ignoredCredentialHosts: Array.from(new Set([
        ...(settings.ignoredCredentialHosts || []),
        host,
      ])).slice(-500),
    });
    await setPendingCredential(null);
    return { host };
  }

  async function saveCredential(payload) {
    const { connection, tenantId: routedTenantId } = await connectionOrThrow(String(payload?.connectionId || ''));
    const tenantId = Number(payload?.tenantId || payload?.tenant_id || routedTenantId || 0);
    const context = await destinations(false, connection.id);
    const requestedContextId = ext.tenantConnectionId(connection.id, tenantId);
    const connectionContext = context.connections.find((row) => row.connectionId === requestedContextId)
      || context.connections.find((row) => Number(row.tenantId) === tenantId)
      || context.connections[0];
    const requestedScope = payload?.scope === 'workspace' ? 'workspace' : 'personal';
    const workspaceId = Number(payload?.workspaceId || 0);
    const vaultId = Number(payload?.vaultId || 0);
    const workspace = connectionContext?.workspaces.find((row) => row.id === workspaceId && row.scope === requestedScope)
      || connectionContext?.workspaces.find((row) => row.id === workspaceId);
    const vault = workspace?.vaults.find((row) => row.id === vaultId);
    if (!workspace || !vault) {
      const error = new Error('Für diesen Workspace oder Unterordner fehlen Schreibrechte.');
      error.code = 'destination_not_writable';
      throw error;
    }

    const credential = {
      entryId: Number(payload?.entryId || 0),
      tenantId: Number(connectionContext?.tenantId || tenantId || 0),
      scope: workspace.scope,
      personal: workspace.scope === 'personal',
      workspaceId,
      vaultId,
      title: String(payload?.title || '').trim(),
      username: String(payload?.username || '').trim(),
      secret: String(payload?.secret || ''),
      url: cleanPageUrl(payload?.url),
      notes: String(payload?.notes || '').trim(),
    };
    if (!credential.title || !credential.secret || !credential.url) {
      throw new Error('Titel, Website und Passwort sind erforderlich.');
    }

    const saved = await ext.saveVaultCredential(connection.baseUrl, connection.apiToken, credential);
    await setPendingCredential(null);
    clearCaches();
    return withoutSecrets({
      ...saved,
      connectionId: connectionContext?.connectionId || requestedContextId,
      authConnectionId: connection.id,
      tenantId: Number(connectionContext?.tenantId || tenantId || 0),
      connectionName: connection.name,
      tenantName: connectionContext?.tenant?.name || connection.tenantName || connection.name,
      workspaceId,
      vaultId,
      scope: workspace.scope,
      personal: workspace.scope === 'personal',
      workspaceName: workspace.name,
      vaultName: vault.name,
    });
  }

  async function contentReady(message, sender) {
    const settings = await ext.getSettings();
    if (!ext.isConfigured(settings)) {
      return { configured: false };
    }

    const pageUrl = String(message?.url || sender?.tab?.url || '');
    const matches = message?.hasPassword ? await getMatches(pageUrl).catch(() => []) : [];
    const exactMatches = matches.filter((entry) => entry.exactDomain);
    const response = {
      configured: true,
      autofillEnabled: settings.autofillEnabled !== false,
      savePromptEnabled: settings.savePromptEnabled !== false,
      matches: matches.map(withoutSecrets),
      pendingCredential: await getPendingCredential(pageUrl),
    };

    if (message?.hasPassword && settings.autofillEnabled !== false && exactMatches.length === 1) {
      const detail = await getEntryDetail(exactMatches[0]).catch(() => null);
      if (detail?.secret) {
        response.autofill = {
          entryId: Number(exactMatches[0].id || 0),
          entryKey: `${exactMatches[0].connectionId}:${exactMatches[0].id}`,
          username: String(detail.username || exactMatches[0].username || ''),
          secret: String(detail.secret || ''),
        };
      }
    }

    const tabId = Number(sender?.tab?.id || 0);
    if (tabId > 0 && browserApi?.action?.setBadgeText) {
      try {
        await Promise.resolve(browserApi.action.setBadgeText({
          tabId,
          text: matches.length ? String(Math.min(matches.length, 99)) : '',
        }));
        await Promise.resolve(browserApi.action.setBadgeBackgroundColor({ tabId, color: '#0875e1' }));
      } catch (_error) {
        // Badge support differs slightly between browsers.
      }
    }
    return response;
  }

  async function handleMessage(message, sender) {
    switch (message?.type) {
      case 'orbitone:get-state': {
        const settings = await ext.getSettings();
        return {
          ok: true,
          configured: ext.isConfigured(settings),
          activeConnectionId: settings.activeConnectionId,
          activeTenantId: Number(settings.activeTenantId || 0),
          connections: (settings.connections || []).map((connection) => ({
            id: connection.id,
            name: connection.name,
            tenantName: connection.tenantName,
            userName: connection.userName,
            baseUrl: connection.baseUrl,
            configured: Boolean(connection.baseUrl && connection.apiToken),
          })),
          settings: {
            autofillEnabled: settings.autofillEnabled,
            savePromptEnabled: settings.savePromptEnabled,
            matchSubdomains: settings.matchSubdomains,
          },
        };
      }
      case 'orbitone:test-connection':
        return { ok: true, context: await loadContext(true, message?.connectionId) };
      case 'orbitone:get-destinations':
        return {
          ok: true,
          ...(await destinations(
            message?.force === true,
            String(message?.connectionId || ''),
            message?.includeAll !== false
          )),
        };
      case 'orbitone:get-matches':
        return {
          ok: true,
          entries: await getMatches(message?.url, {
            force: message?.force === true,
            connectionId: String(message?.connectionId || ''),
          }),
        };
      case 'orbitone:search':
        return {
          ok: true,
          entries: await searchEntries(message?.query, String(message?.connectionId || '')),
        };
      case 'orbitone:get-entry-detail':
        return { ok: true, entry: await getEntryDetail(message?.entry, message?.force === true) };
      case 'orbitone:get-password-history':
        return { ok: true, ...(await getPasswordHistory(message?.entry)) };
      case 'orbitone:save-credential':
        return { ok: true, entry: await saveCredential(message?.credential) };
      case 'orbitone:credentials-detected':
        return { ok: true, ...(await detectCredential(message?.credential)) };
      case 'orbitone:ignore-credential-host':
        return { ok: true, ...(await ignoreCredentialHost(message?.url)) };
      case 'orbitone:content-ready':
        return { ok: true, ...(await contentReady(message, sender)) };
      case 'orbitone:clear-pending':
        await setPendingCredential(null);
        return { ok: true };
      case 'orbitone:settings-updated':
        clearCaches();
        return { ok: true };
      case 'orbitone:set-active-connection': {
        const settings = await settingsOrThrow();
        const requested = ext.splitTenantConnectionId(message?.connectionId);
        if (!settings.connections.some((connection) => connection.id === requested.connectionId)) {
          return { ok: false, error: { code: 'connection_not_found', message: 'Mandant wurde nicht gefunden.' } };
        }
        await ext.saveSettings({
          ...settings,
          activeConnectionId: requested.connectionId,
          activeTenantId: Number(requested.tenantId || message?.tenantId || 0),
        });
        clearCaches();
        return {
          ok: true,
          activeConnectionId: requested.connectionId,
          activeTenantId: Number(requested.tenantId || message?.tenantId || 0),
        };
      }
      case 'orbitone:get-update-state':
        return { ok: true, state: await getUpdateState() };
      case 'orbitone:check-updates':
        return { ok: true, state: await checkForUpdates(true) };
      case 'orbitone:open-update': {
        const state = await getUpdateState();
        if (!state?.downloadUrl) {
          return { ok: false, error: { code: 'no_update_download', message: 'Kein Update-Download verfügbar.' } };
        }
        await ext.extension.tabs.create({ url: state.downloadUrl });
        return { ok: true };
      }
      case 'orbitone:open-entry': {
        const { connection } = await entryConnectionOrThrow(message?.entry);
        await ext.extension.tabs.create({ url: ext.buildAppVaultUrl(connection.baseUrl, message?.entry) });
        return { ok: true };
      }
      case 'orbitone:open-url': {
        const url = ext.normalizeEntryUrl(message?.url || message?.entry?.url);
        if (!url) {
          return { ok: false, error: { code: 'invalid_url', message: ext.t('errorInvalidWebsite', undefined, 'Für diesen Zugang ist keine gültige Website hinterlegt.') } };
        }
        await ext.extension.tabs.create({ url: url.toString() });
        return { ok: true };
      }
      default:
        return { ok: false, error: { code: 'unknown_message', message: 'Unbekannte Extension-Anfrage.' } };
    }
  }

  browserApi.runtime.onMessage.addListener((message, sender, sendResponse) => {
    Promise.resolve(handleMessage(message, sender))
      .then(sendResponse)
      .catch((error) => sendResponse(errorPayload(error)));
    return true;
  });

  browserApi.runtime.onInstalled?.addListener(() => {
    scheduleUpdateChecks().catch(() => null);
    checkForUpdates().catch(() => null);
    ext.getSettings()
      .then((settings) => {
        if (!ext.isConfigured(settings)) {
          return ext.extension.runtime.openOptionsPage();
        }
        return null;
      })
      .catch(() => null);
  });

  browserApi.runtime.onStartup?.addListener(() => {
    scheduleUpdateChecks().catch(() => null);
    checkForUpdates().catch(() => null);
  });

  browserApi.runtime.onUpdateAvailable?.addListener(() => {
    browserApi.runtime.reload();
  });

  browserApi.alarms?.onAlarm?.addListener((alarm) => {
    if (alarm?.name === UPDATE_ALARM) {
      checkForUpdates().catch(() => null);
    }
  });

  scheduleUpdateChecks().catch(() => null);
}());
