import puppeteer from "puppeteer-core";
import { bringPageWindowToFront } from "./etrzxtbsmrmtpfpyetfs.mjs";
import {
  fetchMtuPortfolioFromApi,
  mapMtuApiPolicyItems,
} from "./duymgadbtbrrvujgbqpsldobsrpsttffkvpiesdclzken.mjs";
import {
  prepareHestiaPortfolioPdfEntries,
  runHestiaPortfolioPdfBatch,
} from "./nlemdgbqsotkgporyjjb.mjs";
import {
  PORTFOLIO_MAPPING_PDF_BACKLOG_ONLY_COVERAGE,
  assertPortfolioMappingComplete,
  assertPortfolioPdfBacklogOnlyMappingReport,
  createPortfolioEmissionResult,
  createPortfolioMappingCollector,
  createPortfolioPdfBacklogOnlyEmissionResult,
  createPortfolioPdfBacklogOnlyMappingReport,
} from "./fgroazrhswhewnaqxvgl.mjs";
import {
  createPortfolioPdfTransportIdentity,
  restorePortfolioPdfTransportPolicy,
} from "./abywezhrsiqkqfhujfvq.mjs";

const IHESTIA_URL = "https://ihestia.ergohestia.pl/";
const IAGENT_URL = "https://iagent.ergohestia.pl/";
const IAGENT_HOST = "iagent.ergohestia.pl";







const PORTAL_HOSTNAMES = new Set(["ihestia.ergohestia.pl", IAGENT_HOST]);
const FOREIGN_ERGO_SESSION_HOST = "jupiter.ergohestia.pl";
const PORTAL_TIMEOUT_MS = 3 * 60 * 1000;
const LOGIN_INPUT_SELECTOR = "[id='ihestialogin' i]";
const PASSWORD_INPUT_SELECTOR = "[id='ihestiapassword' i], input[type='password']";

export function validateHestiaCredentials(username, password) {
  const normalizedUsername = String(username || "").trim();
  const normalizedPassword = String(password || "");
  if (!normalizedUsername) throw new Error("Brakuje loginu do Ergo Hestii.");
  if (!normalizedPassword) throw new Error("Brakuje zapisanego hasła do Ergo Hestii.");
  if (normalizedPassword === normalizedUsername) {
    throw new Error("Zapisane hasło Ergo Hestii jest identyczne z loginem. W Ustawieniach użytkownika wpisz ponownie prawidłowe hasło i zapisz zmiany.");
  }
}

function delay(milliseconds) {
  return new Promise((resolve) => globalThis.setTimeout(resolve, milliseconds));
}

function manualInteractionRequired(message) {
  const error = new Error(message);
  error.code = "MANUAL_INTERACTION_REQUIRED";
  return error;
}

function normalizeText(value) {
  return String(value || "")
    .normalize("NFD")
    .replace(/[̀-ͯ]/g, "")
    .replace(/\s+/g, " ")
    .trim()
    .toLowerCase();
}

function getHostname(page) {
  try {
    return new URL(page.url()).hostname.toLowerCase();
  } catch {
    return "";
  }
}

function isOnPortal(page) {
  return PORTAL_HOSTNAMES.has(getHostname(page));
}

async function getCombinedPageText(page) {
  const parts = [];
  for (const frame of page.frames()) {
    try {
      const text = await frame.evaluate(() => String(document.body?.innerText || "").slice(0, 20000));
      if (text) parts.push(text);
    } catch {
    }
  }
  return parts.join(" ").slice(0, 50000);
}

async function findVisibleElement(page, selector) {
  for (const frame of page.frames()) {
    let elements = [];
    try {
      elements = await frame.$$(selector);
      for (const element of elements) {
        const visible = await element.evaluate((candidate) => candidate instanceof HTMLElement
          && candidate.getClientRects().length > 0
          && getComputedStyle(candidate).visibility !== "hidden");
        if (visible) {
          await Promise.all(elements.filter((candidate) => candidate !== element).map((candidate) => candidate.dispose().catch(() => {})));
          return element;
        }
      }
    } catch {
    }
    await Promise.all(elements.map((element) => element.dispose().catch(() => {})));
  }
  return null;
}

async function fillVisibleInputAndPressEnter(page, selector, value, expectedKind) {
  const input = await findVisibleElement(page, selector);
  if (!input) return false;
  try {
    const inputKind = await input.evaluate((candidate) => ({
      isInput: candidate instanceof HTMLInputElement,
      type: String(candidate.getAttribute("type") || "text").toLowerCase(),
    }));
    if (!inputKind.isInput) return false;
    if (expectedKind === "login" && inputKind.type === "password") return false;
    if (expectedKind === "password" && inputKind.type !== "password") return false;
    await input.click({ count: 3 });
    await input.press("Backspace");
    await input.type(value, { delay: 18 });
    await input.press("Enter");
    return true;
  } finally {
    await input.dispose().catch(() => {});
  }
}

async function detectAuthenticationInterruption(page) {
  const pageText = normalizeText(await getCombinedPageText(page));
  if (
    pageText.includes("nieprawidlowy login")
    || pageText.includes("nieprawidlowe haslo")
    || pageText.includes("bledny login")
    || pageText.includes("bledne haslo")
    || pageText.includes("niepoprawne dane logowania")
  ) {
    return { type: "error", message: "Ergo Hestia odrzuciła login lub hasło." };
  }
  if (
    pageText.includes("kod jednorazowy")
    || pageText.includes("kod weryfikacyjny")
    || pageText.includes("uwierzytelnianie dwuskladnikowe")
    || pageText.includes("potwierdz logowanie")
  ) {
    return {
      type: "action",
      title: "Potwierdź logowanie do Ergo Hestii",
      detail: "Zatwierdź logowanie w otwartym oknie (np. kodem SMS).",
    };
  }
  const captchaElement = await findVisibleElement(page, "iframe[src*='captcha'], [class*='captcha']");
  const captchaVisible = Boolean(captchaElement);
  await captchaElement?.dispose().catch(() => {});
  if (pageText.includes("captcha") || captchaVisible) {
    return {
      type: "action",
      title: "Ergo Hestia wymaga potwierdzenia CAPTCHA",
      detail: "Rozwiąż CAPTCHA w otwartym oknie.",
    };
  }
  return null;
}

async function waitForPasswordOrPortal(page) {
  const deadline = Date.now() + 60000;
  while (Date.now() < deadline) {
    if (isOnPortal(page)) return "portal";
    
    
    
    if (getHostname(page) === FOREIGN_ERGO_SESSION_HOST) return "obca-sesja";
    const interruption = await detectAuthenticationInterruption(page);
    if (interruption?.type === "error") throw new Error(interruption.message);
    const passwordInput = await findVisibleElement(page, PASSWORD_INPUT_SELECTOR);
    if (passwordInput) {
      await passwordInput.dispose().catch(() => {});
      return "password";
    }
    await delay(350);
  }
  
  
  throw new Error(`Po podaniu loginu nie pojawił się ekran hasła Ergo Hestii (strona: ${getHostname(page) || "nieznana"}).`);
}



async function upgradeForeignErgoSessionToHestia(page, onProgress) {
  onProgress({
    status: "running",
    title: "Przełączanie na Ergo Hestię…",
    detail: "Na koncie ERGO była otwarta sesja MTU.",
  });
  const deadline = Date.now() + 60000;
  await page.goto(IAGENT_URL, { waitUntil: "domcontentloaded", timeout: 60000 }).catch(() => {});
  while (Date.now() < deadline) {
    if (getHostname(page) === IAGENT_HOST) return true;
    await delay(600);
  }
  return false;
}

async function waitForPortal(page, onProgress, { allowUserInteraction = true } = {}) {
  const startedAt = Date.now();
  let lastActionTitle = "";
  while (Date.now() - startedAt < PORTAL_TIMEOUT_MS) {
    if (isOnPortal(page)) {
      await delay(800);
      return;
    }
    const interruption = await detectAuthenticationInterruption(page);
    if (interruption?.type === "error") throw new Error(interruption.message);
    if (interruption?.type === "action" && !allowUserInteraction) {
      throw manualInteractionRequired(`${interruption.title}. Import w tle został wstrzymany - potrzebne jest widoczne okno Chrome.`);
    }
    if (interruption?.type === "action" && interruption.title !== lastActionTitle) {
      lastActionTitle = interruption.title;
      onProgress({ status: "action_required", title: interruption.title, detail: interruption.detail });
    }
    await delay(500);
  }
  throw new Error("Portal iHestia nie otworzył się w ciągu 3 minut.");
}





async function isStablyOnPortal(page) {
  if (!isOnPortal(page)) return false;
  await delay(1800);
  return isOnPortal(page);
}

async function loginIfNeeded(page, credentials, onProgress, { allowUserInteraction = true } = {}) {
  if (await isStablyOnPortal(page)) return;

  const loginDeadline = Date.now() + 60000;
  let loginReady = false;
  while (Date.now() < loginDeadline) {
    if (isOnPortal(page)) {
      if (await isStablyOnPortal(page)) return;
      continue;
    }
    const passwordInput = await findVisibleElement(page, PASSWORD_INPUT_SELECTOR);
    if (passwordInput) {
      await passwordInput.dispose().catch(() => {});
      break;
    }
    const loginInput = await findVisibleElement(page, LOGIN_INPUT_SELECTOR);
    if (loginInput) {
      await loginInput.dispose().catch(() => {});
      loginReady = true;
      break;
    }
    await delay(350);
  }

  if (loginReady) {
    if (!(await fillVisibleInputAndPressEnter(page, LOGIN_INPUT_SELECTOR, credentials.username, "login"))) {
      throw new Error("Nie udało się wpisać loginu Ergo Hestii.");
    }
    onProgress({
      status: "running",
      title: "Logowanie do Ergo Hestii…",
      detail: "",
    });
  }

  const afterLogin = await waitForPasswordOrPortal(page);
  if (afterLogin === "portal") return;
  if (afterLogin === "obca-sesja") {
    if (await upgradeForeignErgoSessionToHestia(page, onProgress)) return;
    throw new Error("Na koncie ERGO jest otwarta sesja MTU i portal Ergo Hestii nie chce się na nią przełączyć. Zamknij okno MTU (albo uruchom aplikację ponownie) i pobierz polisy Ergo Hestii jako pierwsze.");
  }
  if (!(await fillVisibleInputAndPressEnter(page, PASSWORD_INPUT_SELECTOR, credentials.password, "password"))) {
    throw new Error("Nie udało się wpisać hasła Ergo Hestii.");
  }
  onProgress({
    status: "running",
    title: "Logowanie do Ergo Hestii…",
    detail: "",
  });
  await waitForPortal(page, onProgress, { allowUserInteraction });
}

async function openIagent(page, onProgress) {
  if (getHostname(page) !== IAGENT_HOST) {
    onProgress({
      status: "running",
      title: "Otwieranie portalu…",
      detail: "",
    });
    
    
    
    
    for (let attempt = 0; attempt < 2 && getHostname(page) !== IAGENT_HOST; attempt += 1) {
      try {
        await page.goto(IAGENT_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
      } catch {
      }
      const deadline = Date.now() + 60000;
      while (Date.now() < deadline) {
        if (getHostname(page) === IAGENT_HOST) break;
        await delay(600);
      }
    }
  }
  if (getHostname(page) !== IAGENT_HOST) {
    throw new Error("Panel iAgent (iagent.ergohestia.pl) nie otworzył się na czas. Spróbuj ponownie za chwilę; jeśli błąd wraca, sprawdź uprawnienia konta w iHestii.");
  }
  await delay(1500);
}

function emptyHestiaPdfRetry() {
  return {
    attempted: 0,
    succeeded: 0,
    failed: 0,
    backoff: 0,
    quarantined: 0,
    unsupported: 0,
    deferred: 0,
    remaining: 0,
    batchComplete: true,
    circuitOpen: false,
    circuitCode: null,
    manualRetryAvailable: false,
  };
}

function failedHestiaPdfRetry(remaining, circuitCode = "unknown", deferred = 0) {
  return {
    ...emptyHestiaPdfRetry(),
    deferred: Math.max(0, deferred),
    remaining: Math.max(0, remaining),
    batchComplete: false,
    circuitOpen: true,
    circuitCode,
  };
}

export function buildHestiaImportCompletion({
  freshCount = 0,
  downloadPdfs = false,
  pdfOutcome = null,
  pdfExpectedCount = freshCount,
} = {}) {
  const normalizedFreshCount = Number.isSafeInteger(freshCount) && freshCount >= 0
    ? freshCount
    : 0;
  const normalizedExpectedCount = Number.isSafeInteger(pdfExpectedCount) && pdfExpectedCount >= 0
    ? pdfExpectedCount
    : normalizedFreshCount;
  if (!downloadPdfs) {
    return {
      freshCount: normalizedFreshCount,
      pdfSaved: null,
      pdfRemaining: null,
      pdfRetry: null,
      pdfRetryIdentities: [],
    };
  }
  const hasPdfOutcome = pdfOutcome
    && typeof pdfOutcome === "object"
    && pdfOutcome.pdfRetry
    && typeof pdfOutcome.pdfRetry === "object";
  if (!hasPdfOutcome) {
    return {
      freshCount: normalizedFreshCount,
      pdfSaved: 0,
      pdfRemaining: normalizedExpectedCount,
      pdfRetry: failedHestiaPdfRetry(normalizedExpectedCount, "write_failed"),
      pdfRetryIdentities: [],
    };
  }
  return {
    freshCount: normalizedFreshCount,
    pdfSaved: Number.isSafeInteger(pdfOutcome.saved) ? pdfOutcome.saved : 0,
    pdfRemaining: Number.isSafeInteger(pdfOutcome.remaining) ? pdfOutcome.remaining : 0,
    pdfRetry: pdfOutcome.pdfRetry,
    pdfRetryIdentities: Array.isArray(pdfOutcome.pdfRetryIdentities)
      ? pdfOutcome.pdfRetryIdentities
      : [],
  };
}

export async function runHestiaPortfolioPdfPhase({
  page,
  retryStore,
  pdfDirectory,
  policies,
  onProgress = () => {},
  limit = 50,
  skipPolicyNumbers = [],
  knownPolicyNumbers = [],
} = {}) {
  if (!Number.isSafeInteger(limit) || limit <= 0) {
    throw new TypeError("limit musi byc dodatnia liczba calkowita.");
  }
  const eligible = prepareHestiaPortfolioPdfEntries({
    policies,
    skipPolicyNumbers,
    knownPolicyNumbers,
  });
  const pdfDirectoryReady = typeof pdfDirectory === "string"
    && pdfDirectory.trim().length > 0
    && !pdfDirectory.includes("\0");
  if (!pdfDirectoryReady) {
    const pdfRetry = failedHestiaPdfRetry(
      eligible.length,
      "write_failed",
      Math.max(0, eligible.length - Math.min(eligible.length, limit)),
    );
    console.error(
      `Hestia PDF partia: brak katalogu docelowego (write_failed), pozostalo=${pdfRetry.remaining}`,
    );
    onProgress({
      status: "running",
      title: "Pobieranie PDF-ow przerwane",
      detail: "Faza dokumentow zostala bezpiecznie zatrzymana; polisy nadal zostana zaimportowane.",
    });
    return {
      attempted: 0,
      saved: 0,
      remaining: pdfRetry.remaining,
      pdfRetry,
      pdfRetryIdentities: [],
    };
  }
  if (eligible.length === 0) {
    onProgress({
      status: "running",
      title: "Pobieranie PDF-ow pominiete",
      detail: "Wszystkie polisy Hestii maja juz podpiete pliki.",
    });
    return {
      attempted: 0,
      saved: 0,
      remaining: 0,
      pdfRetry: emptyHestiaPdfRetry(),
      pdfRetryIdentities: [],
    };
  }

  const planned = Math.min(eligible.length, limit);
  onProgress({
    status: "running",
    title: "Pobieranie PDF-ow...",
    detail: `Do rozpatrzenia w tej partii: ${planned} plikow.`,
  });
  const retryStoreReady = retryStore
    && typeof retryStore.getDecisions === "function"
    && typeof retryStore.recordFailures === "function"
    && typeof retryStore.recordSuccesses === "function"
    && typeof retryStore.markUnsupported === "function";
  let batch;
  if (!retryStoreReady) {
    batch = {
      publicResult: failedHestiaPdfRetry(
        eligible.length,
        "store_failed",
        Math.max(0, eligible.length - planned),
      ),
      privateResult: {
        manualRetryIdentities: [],
      },
    };
  } else {
    try {
      batch = await runHestiaPortfolioPdfBatch({
        page: page && typeof page.evaluate === "function" ? page : null,
        retryStore,
        pdfDirectory,
        policies,
        limit,
        skipPolicyNumbers,
        knownPolicyNumbers,
        onProgress({ processed, planned: batchPlanned }) {
          if (processed % 5 === 0 || processed === batchPlanned) {
            onProgress({
              status: "running",
              title: `Pobieranie PDF-ow... (${processed}/${batchPlanned})`,
              detail: "",
            });
          }
        },
      });
    } catch {
      
      
      batch = {
        publicResult: failedHestiaPdfRetry(
          eligible.length,
          "unknown",
          Math.max(0, eligible.length - planned),
        ),
        privateResult: {
          manualRetryIdentities: [],
        },
      };
    }
  }

  const pdfRetry = batch.publicResult;
  
  
  console.error(
    `Hestia PDF partia: rozmiar=${planned} attempted=${pdfRetry.attempted} ok=${pdfRetry.succeeded}`
    + ` failed=${pdfRetry.failed} backoff=${pdfRetry.backoff} unsupported=${pdfRetry.unsupported}`
    + ` obwod=${pdfRetry.circuitOpen ? pdfRetry.circuitCode || "unknown" : "-"}`
    + ` pozostalo=${pdfRetry.remaining}`,
  );
  onProgress({
    status: "running",
    title: pdfRetry.circuitOpen
      ? "Pobieranie PDF-ow przerwane"
      : "PDF-y pobrane",
    detail: pdfRetry.circuitOpen
      ? "Faza dokumentow zostala bezpiecznie zatrzymana; polisy nadal zostana zaimportowane."
      : `Zapisano ${pdfRetry.succeeded} z ${pdfRetry.attempted} rozpoczetych plikow.`,
  });
  return {
    attempted: pdfRetry.attempted,
    saved: pdfRetry.succeeded,
    remaining: pdfRetry.remaining,
    pdfRetry,
    pdfRetryIdentities: batch.privateResult.manualRetryIdentities,
  };
}

export async function finalizeHestiaPortfolioApiImport({
  apiPolicies,
  mappingReport = null,
  sinceDate = "",
  downloadPdfs = false,
  page,
  retryStore,
  pdfDirectory = "",
  pdfPolicyLimit = 50,
  skipPdfPolicyNumbers = [],
  knownPdfPolicyNumbers = [],
  onProgress = () => {},
  onCompletion,
} = {}) {
  if (!Array.isArray(apiPolicies)) {
    throw new TypeError("apiPolicies musi byc tablica.");
  }
  const pdfEligibleEntries = downloadPdfs
    ? prepareHestiaPortfolioPdfEntries({
      policies: apiPolicies,
      skipPolicyNumbers: skipPdfPolicyNumbers,
      knownPolicyNumbers: knownPdfPolicyNumbers,
    })
    : [];
  const pdfExpectedCount = pdfEligibleEntries.length;
  const pdfOutcome = downloadPdfs
    ? await runHestiaPortfolioPdfPhase({
      page,
      retryStore,
      pdfDirectory,
      policies: apiPolicies,
      onProgress,
      limit: pdfPolicyLimit,
      skipPolicyNumbers: skipPdfPolicyNumbers,
      knownPolicyNumbers: knownPdfPolicyNumbers,
    })
    : null;
  
  
  
  const pdfQueuePolicyNumbers = downloadPdfs
    ? pdfEligibleEntries
      .filter((entry) => entry?.policy?.pdfAvailable !== true)
      .map((entry) => createPortfolioPdfTransportIdentity(entry.policy, {
        insurer: "hestia",
      }))
      .slice(0, 10000)
    : [];
  const completionMappingReport = mappingReport?.coverage
    === PORTFOLIO_MAPPING_PDF_BACKLOG_ONLY_COVERAGE
    ? assertPortfolioPdfBacklogOnlyMappingReport(mappingReport)
    : assertPortfolioMappingComplete(mappingReport);
  const { emittedPolicies, emissionReport } = completionMappingReport.coverage
    === PORTFOLIO_MAPPING_PDF_BACKLOG_ONLY_COVERAGE
    ? createPortfolioPdfBacklogOnlyEmissionResult(
        apiPolicies.filter((policy) => (
          String(policy?.insurer || "").trim().toUpperCase() === "HESTIA"
          && policy.pdfAvailable === true
        )),
      )
    : createPortfolioEmissionResult(apiPolicies, {
        sinceDate,
        getFreshnessDates: (policy) => [policy?.issueDate, policy?.validFrom],
        isBacklogPolicy: (policy) => (
          String(policy?.insurer || "").trim().toUpperCase() === "HESTIA"
          && policy?.pdfAvailable === true
        ),
      });
  const completion = buildHestiaImportCompletion({
    freshCount: emissionReport.freshCount,
    downloadPdfs,
    pdfOutcome,
    pdfExpectedCount,
  });
  onCompletion?.({
    ...completion,
    pdfQueuePolicyNumbers,
    mappingReport: completionMappingReport,
    emissionReport,
  });
  return emittedPolicies;
}

export function restoreHestiaPortfolioPdfQueue(values) {
  return (Array.isArray(values) ? values : [])
    .map((value) => restorePortfolioPdfTransportPolicy(value, {
      insurer: "hestia",
      insurerLabel: "Hestia",
    }))
    .filter(Boolean);
}

export function createHestiaAutomation({ browserPool, pdfRetryStore }) {
  const INSURER_KEY = "hestia";
  let lastVisiblePage = null;

  
  
  async function openPage(headless) {
    return browserPool.openPage({ insurerKey: INSURER_KEY, headless });
  }

  
  function isVisibleBrowserActive() {
    return browserPool.isHeadless(INSURER_KEY) === false;
  }

  async function importPortfolioAttempt({
    username,
    password,
    downloadPdfs = false,
    pdfDirectory = "",
    pdfPolicyLimit = 50,
    skipPdfPolicyNumbers = [],
    knownPdfPolicyNumbers = [],
    pdfBacklogOnly = false,
    pdfBacklogPolicyNumbers = [],
    sinceDate = "",
    onCompletion,
    onProgress,
  }, { headless = false } = {}) {
    validateHestiaCredentials(username, password);
    const page = await openPage(headless);
    page.setDefaultTimeout(12000);
    page.setDefaultNavigationTimeout(60000);
    if (!headless) {
      lastVisiblePage = page;
      await bringPageWindowToFront(page);
    }
    try {
      onProgress({
        status: "running",
        title: headless ? "Import polis Ergo Hestii działa w tle…" : "Otwieranie portalu Ergo Hestii…",
        detail: "",
      });
      try {
        await page.goto(IHESTIA_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
      } catch {
        
        
        await page.goto(IHESTIA_URL, { waitUntil: "domcontentloaded", timeout: 60000 }).catch(() => {});
      }
      await loginIfNeeded(page, { username, password }, onProgress, { allowUserInteraction: !headless });
      await openIagent(page, onProgress);

      
      
      
      
      if (pdfBacklogOnly) {
        const backlogPolicies = restoreHestiaPortfolioPdfQueue(pdfBacklogPolicyNumbers);
        if (backlogPolicies.length === 0) {
          throw new Error("Brak zaległych dokumentów do pobrania.");
        }
        onProgress({
          status: "running",
          title: "Pobieranie zaległych PDF-ów…",
          detail: `W kolejce: ${backlogPolicies.length} plików.`,
        });
        return await finalizeHestiaPortfolioApiImport({
          apiPolicies: backlogPolicies,
          mappingReport: createPortfolioPdfBacklogOnlyMappingReport(),
          downloadPdfs: true,
          page,
          retryStore: pdfRetryStore,
          pdfDirectory,
          pdfPolicyLimit,
          skipPdfPolicyNumbers,
          knownPdfPolicyNumbers,
          onProgress,
          onCompletion,
        });
      }

      onProgress({
        status: "running",
        title: "Zalogowano do Ergo Hestii",
        detail: "Pobieranie polis…",
      });
      const listingProgress = ({ total }) => {
        onProgress({
          status: "running",
          title: "Pobieranie polis…",
          detail: `Sprawdzono ${total} polis.`,
        });
      };
      let apiItems;
      try {
        apiItems = await fetchMtuPortfolioFromApi(page, { onProgress: listingProgress });
      } catch {
        
        
        
        await page.goto(IHESTIA_URL, { waitUntil: "domcontentloaded", timeout: 60000 }).catch(() => {});
        await loginIfNeeded(page, { username, password }, onProgress, { allowUserInteraction: !headless });
        await openIagent(page, onProgress);
        apiItems = await fetchMtuPortfolioFromApi(page, { onProgress: listingProgress });
      }
      const mappingCollector = createPortfolioMappingCollector(apiItems.length);
      const apiPolicies = mapMtuApiPolicyItems(apiItems, { mappingCollector });
      const mappingReport = assertPortfolioMappingComplete(mappingCollector.createReport());
      if (apiPolicies.length === 0) {
        throw new Error("Nie znaleziono żadnej aktywnej polisy. Sprawdź, czy konto ma dostęp do portfela.");
      }
      const previewEmission = createPortfolioEmissionResult(apiPolicies, {
        sinceDate,
        getFreshnessDates: (policy) => [policy?.issueDate, policy?.validFrom],
      });
      onProgress({
        status: "running",
        title: "Porządkowanie pobranych polis…",
        detail: sinceDate
          ? `Nowych polis: ${previewEmission.emissionReport.freshCount}.`
          : `Sprawdzono ${previewEmission.emissionReport.freshCount} polis.`,
      });
      
      
      
      return await finalizeHestiaPortfolioApiImport({
        apiPolicies,
        mappingReport,
        sinceDate,
        downloadPdfs,
        page,
        retryStore: pdfRetryStore,
        pdfDirectory,
        pdfPolicyLimit,
        skipPdfPolicyNumbers,
        knownPdfPolicyNumbers,
        onProgress,
        onCompletion,
      });
    } finally {
      if (headless) await page.close().catch(() => {});
      else lastVisiblePage = page;
    }
  }

  
  
  async function importPortfolio(options) {
    const startHeadless = isVisibleBrowserActive() ? false : true;
    if (!startHeadless) return importPortfolioAttempt(options, { headless: false });
    try {
      return await importPortfolioAttempt(options, { headless: true });
    } catch (error) {
      if (error?.code !== "MANUAL_INTERACTION_REQUIRED") throw error;
      options?.onProgress?.({
        status: "running",
        title: "Potrzebne jest okno przeglądarki…",
        detail: "Dokończ logowanie w oknie, które się otworzy.",
      });
      return importPortfolioAttempt(options, { headless: false });
    }
  }

  async function focusWindow() {
    const candidates = [];
    if (lastVisiblePage && !lastVisiblePage.isClosed()) candidates.push(lastVisiblePage);
    
    
    if (browserPool.isHeadless(INSURER_KEY) === false) {
      for (const page of [...browserPool.listPages(INSURER_KEY)].reverse()) {
        if (page && !page.isClosed?.() && !candidates.includes(page)) candidates.push(page);
      }
    }
    for (const candidate of candidates) {
      if (await bringPageWindowToFront(candidate)) return true;
    }
    return false;
  }

  
  
  async function close() {
    lastVisiblePage = null;
    await browserPool.closeInsurer(INSURER_KEY);
  }

  return { importPortfolio, focusWindow, close };
}
