import puppeteer from "puppeteer-core";
import { bringPageWindowToFront, composeKnownAnswerResolvers, buildKnownVehicleAnswerResolver } from "./etrzxtbsmrmtpfpyetfs.mjs";
import { attachClientInfoOverlay } from "./hkwnbsimckiekhsfxcto.mjs";
import {
  enrichAllianzPolicyItems,
  fetchAllianzPolicies,
  getAllianzPortfolioMinEndDate,
  mapAllianzPolicies,
} from "./bjbwhhtftoqmzjtwlhze.mjs";
import { runAllianzPortfolioPdfBatch } from "./xyoykvhbzzqocztxulyd.mjs";
import {
  assertPortfolioMappingComplete,
  createPortfolioEmissionResult,
  createPortfolioMappingCollector,
} from "./fgroazrhswhewnaqxvgl.mjs";

const ALLIANZ_URL = "http://chuck.allianz.pl/";
const ALLIANZ_POLICY_LIST_PAGE_URL = "https://chuck.allianz.pl/web/guest/polisy";


const ALLIANZ_MAJATEK_URL = "https://chuck.allianz.pl/web/guest/framework/29";
const ESAMOCHOD_HOST = "e-samochod.allianz.pl";

const MANUAL_FULL_PATH_TIMEOUT_MS = 15 * 60 * 1000;


const LOGIN_WAIT_TIMEOUT_MS = 10 * 60 * 1000;

const HEADLESS_LOGIN_TIMEOUT_MS = 90 * 1000;


const LOGIN_SELECTOR = "input[id$='_login'], input[name$='_login'], input[name*='login' i], input[id*='login' i], input[autocomplete='username'], form input[type='text']";
const PASSWORD_SELECTOR = "input[type='password']";
const REMEMBER_ME_SELECTOR = "input[id$='_rememberMe'], input[name$='_rememberMe']";

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

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

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

export function isAllianzPortalUrl(value) {
  try {
    const hostname = new URL(value).hostname.toLowerCase();
    return hostname === "allianz.pl" || hostname.endsWith(".allianz.pl");
  } catch {
    return false;
  }
}

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 frameHasSelector(page, selector) {
  const element = await findVisibleElement(page, selector);
  const found = Boolean(element);
  await element?.dispose().catch(() => {});
  return found;
}

async function findLoginButton(page) {
  for (const frame of page.frames()) {
    let buttons = [];
    try {
      buttons = await frame.$$("button, input[type='submit']");
      for (const button of buttons) {
        const state = await button.evaluate((candidate) => ({
          text: String(candidate.innerText || candidate.value || candidate.textContent || "")
            .normalize("NFD")
            .replace(/[̀-ͯ]/g, "")
            .replace(/\s+/g, " ")
            .trim()
            .toLowerCase(),
          visible: candidate instanceof HTMLElement
            && candidate.getClientRects().length > 0
            && getComputedStyle(candidate).visibility !== "hidden",
        }));
        if (state.visible && /^zaloguj( sie)?$/.test(state.text)) {
          await Promise.all(buttons
            .filter((candidate) => candidate !== button)
            .map((candidate) => candidate.dispose().catch(() => {})));
          return button;
        }
      }
    } catch {
    }
    await Promise.all(buttons.map((button) => button.dispose().catch(() => {})));
  }
  return null;
}

async function isPortalAuthenticated(page) {
  if (!isAllianzPortalUrl(page.url())) return false;
  if (await frameHasSelector(page, PASSWORD_SELECTOR)) return false;
  
  
  return page.mainFrame().evaluate(() => {
    if (document.body?.classList.contains("signed-in")) return true;
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim().toLowerCase();
    return [...document.querySelectorAll("a, button")]
      .some((element) => compact(element.innerText).startsWith("wyloguj"));
  }).catch(() => false);
}

async function detectLoginInterruption(page) {
  const pageText = normalizeText(await getCombinedPageText(page));
  
  
  if (
    pageText.includes("rejestracja urzadzenia")
    || pageText.includes("dodaj to urzadzenie")
    || pageText.includes("zaufane urzadzenie")
    || pageText.includes("dodaj urzadzenie")
    || pageText.includes("dodaj komputer")
    || pageText.includes("zapamietaj to urzadzenie")
    || pageText.includes("dodaj do zaufanych")
  ) {
    return {
      type: "action",
      title: "Dodaj ten komputer w oknie Allianz",
      detail: "Kliknij „DODAJ DO ZAUFANYCH”, aby przez 30 dni logować się bez SMS-a.",
    };
  }
  if (
    pageText.includes("kod sms")
    || pageText.includes("kod jednorazowy")
    || pageText.includes("kod weryfikacyjny")
    || pageText.includes("kod autoryzacyjny")
    || pageText.includes("wpisz kod")
    || pageText.includes("wprowadz kod")
    || pageText.includes("uwierzytelnianie dwuskladnikowe")
  ) {
    return {
      type: "action",
      title: "Wpisz kod SMS w oknie Allianz",
      detail: "Portal Allianz wysłał kod SMS. Przepisz go w otwartym oknie.",
    };
  }
  if (
    pageText.includes("uwierzytelnianie nie powiodlo sie")
    || pageText.includes("nieprawidlowy login lub haslo")
    || pageText.includes("nieprawidlowe haslo")
    || pageText.includes("bledne dane logowania")
    || pageText.includes("konto zostalo zablokowane")
  ) {
    return {
      type: "action",
      title: "Sprawdź login i hasło do Allianz",
      detail: "Portal nie przyjął danych logowania. Popraw je w otwartym oknie lub w ustawieniach użytkownika.",
    };
  }
  if (pageText.includes("captcha") || await frameHasSelector(page, "iframe[src*='captcha'], [class*='captcha']")) {
    return {
      type: "action",
      title: "Allianz wymaga potwierdzenia CAPTCHA",
      detail: "Rozwiąż CAPTCHA w otwartym oknie.",
    };
  }
  return null;
}

async function fillLoginForm(page, credentials, onProgress) {
  const login = await findVisibleElement(page, LOGIN_SELECTOR);
  const password = await findVisibleElement(page, PASSWORD_SELECTOR);
  const submit = await findLoginButton(page);
  if (!login || !password) {
    await Promise.all([login, password, submit].filter(Boolean).map((element) => element.dispose().catch(() => {})));
    return false;
  }
  try {
    await login.click({ clickCount: 3 });
    await login.type(credentials.username, { delay: 18 });
    await password.click({ clickCount: 3 });
    await password.type(credentials.password, { delay: 18 });
    const valuesWereEntered = await Promise.all([
      login.evaluate((element, expected) => element instanceof HTMLInputElement && element.value === expected, credentials.username),
      password.evaluate((element, expected) => element instanceof HTMLInputElement && element.value === expected, credentials.password),
    ]);
    if (!valuesWereEntered.every(Boolean)) {
      throw new Error("Nie udało się wpisać loginu lub hasła do formularza Allianz.");
    }
    
    const rememberMe = await findVisibleElement(page, REMEMBER_ME_SELECTOR);
    if (rememberMe) {
      await rememberMe.evaluate((element) => {
        if (element instanceof HTMLInputElement && !element.checked) element.click();
      }).catch(() => {});
      await rememberMe.dispose().catch(() => {});
    }
    onProgress({ status: "running", title: "Logowanie do Allianz…", detail: "" });
    if (submit) {
      await submit.click();
    } else {
      await password.evaluate((element) => element.form?.requestSubmit?.() ?? element.form?.submit?.());
    }
    return true;
  } finally {
    await Promise.all([login, password, submit].filter(Boolean).map((element) => element.dispose().catch(() => {})));
  }
}

 
export function peselToAllianzBirthDate(pesel) {
  const digits = String(pesel || "").replace(/\D/g, "");
  if (digits.length !== 11) return "";
  let year = Number(digits.slice(0, 2));
  let month = Number(digits.slice(2, 4));
  const day = Number(digits.slice(4, 6));
  if (month >= 81) { year += 1800; month -= 80; }
  else if (month >= 61) { year += 2200; month -= 60; }
  else if (month >= 41) { year += 2100; month -= 40; }
  else if (month >= 21) { year += 2000; month -= 20; }
  else year += 1900;
  if (month < 1 || month > 12 || day < 1 || day > 31) return "";
  return `${String(day).padStart(2, "0")}/${String(month).padStart(2, "0")}/${year}`;
}

const ALLIANZ_AMOUNT_SOURCE = "(?:\\d{1,3}(?:[\\s\\u00a0\\u202f]\\d{3})*|\\d+)(?:,\\d{2})?\\s*zł";
const ALLIANZ_AMOUNT_PATTERN = new RegExp(ALLIANZ_AMOUNT_SOURCE, "gi");

 
export function normalizeAllianzRiskForVariant(labelValue, cellValue) {
  const sourceLabel = String(labelValue || "").replace(/\s+/g, " ").trim();
  const sourceValue = String(cellValue || "").replace(/\s+/g, " ").trim();
  const normalizedLabel = normalizeText(sourceLabel);
  const normalizedValue = normalizeText(sourceValue);
  if (!sourceLabel || !sourceValue
    || /^(?:-|—|nie|brak|nie dotyczy|n\/a)$/.test(normalizedValue)) {
    return null;
  }
  const money = sourceValue.match(ALLIANZ_AMOUNT_PATTERN)?.[0] || "";
  const numericMoney = money
    ? Number(money.replace(/\s|zł/gi, "").replace(",", "."))
    : null;
  const explicitlyPremium = /\bskladk|cena|koszt\b/.test(normalizedLabel);
  const includedAmount = money && explicitlyPremium ? money : "w cenie";

  if (/^oc\b/.test(normalizedLabel)) {
    return { label: "OC", amount: money || "w cenie" };
  }
  if (/^(?:autocasco|ac)\b/.test(normalizedLabel)) {
    const variant = /\baso\b|\bserwis/.test(`${normalizedLabel} ${normalizedValue}`)
      ? "ASO/serwisowy"
      : /\bkosztorys/.test(`${normalizedLabel} ${normalizedValue}`)
        ? "kosztorys"
        : "";
    const insuranceSum = money && numericMoney >= 5_000 && !explicitlyPremium ? money : "";
    const details = [insuranceSum, variant].filter(Boolean);
    return {
      label: details.length > 0 ? `AC (${details.join(", ")})` : "AC",
      amount: insuranceSum ? "w cenie" : includedAmount,
    };
  }
  if (/^nnw\b/.test(normalizedLabel)) {
    return {
      label: "NNW",
      amount: money && numericMoney < 5_000 ? money : "w cenie",
    };
  }
  if (/^assistance\b/.test(normalizedLabel)) {
    const nameFromLabel = sourceLabel.replace(/^.*?\bassistance\b/i, "").trim();
    const nameFromValue = money || /^(?:tak|w cenie|included|yes)$/i.test(sourceValue)
      ? ""
      : sourceValue;
    const name = (nameFromLabel || nameFromValue)
      .replace(/^[\s:;,./+–—-]+|[\s:;,./+–—-]+$/g, "")
      .slice(0, 40);
    return {
      label: name ? `Assistance (${name})` : "Assistance",
      amount: includedAmount,
    };
  }
  if (/^(?:auto)?szyb/.test(normalizedLabel) || /^ubezpieczenie szyb/.test(normalizedLabel)) {
    const insuranceSum = money && numericMoney >= 1_000 && !explicitlyPremium ? money : "";
    return {
      label: insuranceSum ? `Szyby (${insuranceSum})` : "Szyby",
      amount: insuranceSum ? "w cenie" : includedAmount,
    };
  }
  return null;
}

export function buildAllianzPremiums(snapshot) {
  const amounts = [...String(snapshot?.skladkaText || "").matchAll(ALLIANZ_AMOUNT_PATTERN)]
    .map((match) => match[0].replace(/[  ]/g, " ").trim());
  if (amounts.length === 0) return [];
  const headers = Array.isArray(snapshot?.headers) ? snapshot.headers : [];
  const rows = Array.isArray(snapshot?.rows) ? snapshot.rows : [];
  const chosenIndex = Number.isInteger(snapshot?.chosenIndex) ? snapshot.chosenIndex : -1;
  return amounts.map((amount, index) => {
    const label = headers[index] || `Pakiet ${index + 1}`;
    const risks = rows
      .map((row) => ({
        label: String(row?.label || "").trim(),
        values: Array.isArray(row?.values) ? row.values : row?.amounts,
      }))
      .filter((row) => Array.isArray(row.values) && row.values.length === amounts.length && row.values[index])
      .map((row) => normalizeAllianzRiskForVariant(row.label, row.values[index]))
      .filter(Boolean);
    const comparableRisks = [];
    const seenRiskLabels = new Set();
    for (const risk of risks) {
      const key = normalizeText(risk.label).match(/^(?:oc|ac|nnw|assistance|szyb)/)?.[0] || "";
      if (!key || seenRiskLabels.has(key)) continue;
      seenRiskLabels.add(key);
      comparableRisks.push(risk);
    }
    const findRisk = (pattern) => comparableRisks.find((risk) => pattern.test(risk.label))?.amount || "";
    return {
      insurer: "Allianz",
      label: index === chosenIndex ? `${label} (wybrano)` : label,
      amount,
      details: comparableRisks.length > 0
        ? comparableRisks.map((risk) => `${risk.label} ${risk.amount}`).join(" • ")
        : "Allianz - zakres ubezpieczenia",
      components: {
        oc: findRisk(/^OC\b/i),
        ac: findRisk(/^(Autocasco|AC)\b/i),
        vehicleValue: "",
        assistance: findRisk(/^Assistance\b/i),
        nnw: findRisk(/^NNW\b/i),
        szyby: findRisk(/^Szyby\b/i),
      },
      componentsList: comparableRisks.slice(0, 12),
    };
  });
}

 
async function readAllianzOfferSnapshot(page) {
  return page.evaluate((amountSource) => {
    const amountRegex = new RegExp(amountSource, "gi");
    const compact = (value) => String(value || "").replace(/[  ]/g, " ").replace(/\s+/g, " ").trim();
    const visible = (element) => element instanceof HTMLElement
      && element.getClientRects().length > 0
      && getComputedStyle(element).visibility !== "hidden";
    const allElements = [...document.querySelectorAll("body *")].filter(visible);

    const skladkaCandidates = allElements
      .map((element) => ({ element, text: compact(element.innerText) }))
      .filter(({ text }) => /^SKŁADKA\b/.test(text) && [...text.matchAll(amountRegex)].length > 0);
    
    const skladkaRow = skladkaCandidates
      .filter(({ element }) => !skladkaCandidates.some((other) => other.element !== element && element.contains(other.element)))
      .map(({ element, text }) => ({ element, text }))[0] || null;
    const skladkaRowText = skladkaRow?.text || "";
    const amountCount = [...skladkaRowText.matchAll(amountRegex)].length;
    const amountLeaves = skladkaRow
      ? [...skladkaRow.element.querySelectorAll("*")]
        .filter(visible)
        .map((element) => ({ element, text: compact(element.innerText || element.textContent) }))
        .filter(({ text }) => new RegExp(`^${amountSource}$`, "i").test(text))
        .filter(({ element }) => ![...element.children].some((child) => (
          visible(child) && new RegExp(amountSource, "i").test(compact(child.innerText || child.textContent))
        )))
      : [];
    const columnCenters = amountLeaves
      .slice(0, amountCount)
      .map(({ element }) => {
        const rect = element.getBoundingClientRect();
        return rect.left + (rect.width / 2);
      });

    const headers = allElements
      .filter((element) => element.children.length === 0)
      .map((element) => compact(element.innerText))
      .filter((text) => /^[A-ZĄĆĘŁŃÓŚŹŻ]{3,12}$/.test(text) && text !== "SKŁADKA");
    const uniqueHeaders = [...new Set(headers)];

    
    const packageButtons = allElements
      .filter((element) => element.tagName === "BUTTON")
      .map((element) => compact(element.innerText))
      .filter((text) => /^(Wybierz|Wybrany)$/.test(text));
    const chosenIndex = packageButtons.findIndex((text) => text === "Wybrany");

    const rowCandidates = allElements
      .map((element) => ({ element, text: compact(element.innerText) }))
      .filter(({ text }) => {
        if (!text || text.length > 220 || /^SKŁADKA\b/.test(text)) return false;
        const matches = [...text.matchAll(amountRegex)];
        if (amountCount === 0 || matches.length !== amountCount) return false;
        const firstAmountIndex = text.search(new RegExp(amountSource, "i"));
        return firstAmountIndex > 2;
      });
    const leafRows = rowCandidates
      .filter(({ element }) => !rowCandidates.some((other) => other.element !== element && element.contains(other.element)));
    const seenRows = new Set();
    const rows = [];
    for (const { text } of leafRows) {
      if (seenRows.has(text)) continue;
      seenRows.add(text);
      const matches = [...text.matchAll(amountRegex)].map((match) => match[0]);
      const label = text.slice(0, text.indexOf(matches[0])).trim();
      rows.push({ label, amounts: matches });
    }
    const describeCell = (element) => {
      const text = compact(element.innerText || element.textContent);
      if (text) return text;
      const described = compact(
        element.getAttribute("aria-label")
          || element.getAttribute("title")
          || element.getAttribute("data-label")
          || "",
      );
      if (described) return described;
      const checked = element.matches("[aria-checked='true'], input:checked")
        || element.querySelector("[aria-checked='true'], input:checked");
      const visualCheck = /(?:check|tick|included|active|selected|yes)/i.test(String(element.className || ""))
        || element.querySelector("svg, [class*='check'], [class*='tick']");
      return checked || visualCheck ? "w cenie" : "";
    };
    if (columnCenters.length === amountCount && amountCount > 0) {
      const riskLabelPattern = /^(?:OC\b|Autocasco\b|AC\b|NNW\b|Assistance\b|(?:Auto)?Szyby\b|Ubezpieczenie szyb\b)/i;
      const riskLabels = allElements
        .filter((element) => element.children.length === 0)
        .map((element) => ({ element, label: compact(element.innerText || element.textContent) }))
        .filter(({ label }) => riskLabelPattern.test(label) && label.length <= 90);
      const structuralRows = [];
      for (const { element: labelElement, label } of riskLabels) {
        let rowElement = labelElement;
        let values = [];
        for (let level = 0; level < 6 && rowElement.parentElement; level += 1) {
          rowElement = rowElement.parentElement;
          const rowRect = rowElement.getBoundingClientRect();
          if (rowRect.height > 180 || rowRect.width < 250) continue;
          const descendants = [...rowElement.querySelectorAll("*")]
            .filter(visible)
            .filter((candidate) => candidate !== labelElement && !labelElement.contains(candidate))
            .map((candidate) => {
              const rect = candidate.getBoundingClientRect();
              return {
                candidate,
                rect,
                area: Math.max(1, rect.width * rect.height),
              };
            });
          values = columnCenters.map((center) => {
            const candidates = descendants
              .filter(({ rect }) => (
                rect.left - 3 <= center
                && rect.right + 3 >= center
                && rect.width <= rowRect.width * 0.38
              ))
              .sort((left, right) => left.area - right.area);
            for (const { candidate } of candidates) {
              const value = describeCell(candidate);
              if (value && value !== label) return value;
            }
            return "";
          });
          if (values.some(Boolean)) break;
        }
        if (values.length === amountCount && values.some(Boolean)) {
          structuralRows.push({ label, values });
        }
      }
      for (const structuralRow of structuralRows) {
        const existingIndex = rows.findIndex((row) => compact(row.label).toLowerCase()
          === compact(structuralRow.label).toLowerCase());
        if (existingIndex < 0) {
          rows.push(structuralRow);
          continue;
        }
        const currentValues = rows[existingIndex].values || rows[existingIndex].amounts || [];
        rows[existingIndex] = {
          ...rows[existingIndex],
          values: structuralRow.values.map((value, index) => value || currentValues[index] || ""),
        };
      }
    }
    return { headers: uniqueHeaders, skladkaText: skladkaRowText, rows, chosenIndex };
  }, ALLIANZ_AMOUNT_SOURCE);
}

export async function extractAllianzPremiums(page) {
  return buildAllianzPremiums(await readAllianzOfferSnapshot(page));
}

 
async function ensureAuthenticated(page, credentials, { allowUserInteraction, onProgress }) {
  if (await isPortalAuthenticated(page)) return { alreadyLoggedIn: true };
  const deadline = Date.now() + (allowUserInteraction ? LOGIN_WAIT_TIMEOUT_MS : HEADLESS_LOGIN_TIMEOUT_MS);
  let formFilled = false;
  let lastActionTitle = "";
  while (Date.now() < deadline) {
    if (page.isClosed?.()) {
      throw manualInteractionRequired("Okno Allianz zostało zamknięte przed dokończeniem logowania.");
    }
    if (await isPortalAuthenticated(page)) return { alreadyLoggedIn: false };
    if (!formFilled && await frameHasSelector(page, PASSWORD_SELECTOR)) {
      formFilled = await fillLoginForm(page, credentials, onProgress);
      await delay(1200);
      continue;
    }
    const interruption = await detectLoginInterruption(page).catch(() => null);
    if (interruption?.type === "action") {
      if (!allowUserInteraction) {
        throw manualInteractionRequired(`${interruption.title}. Import w tle został wstrzymany - okno Chrome otworzy się do dokończenia logowania.`);
      }
      if (interruption.title !== lastActionTitle) {
        lastActionTitle = interruption.title;
        onProgress({ status: "action_required", title: interruption.title, detail: interruption.detail });
      }
    }
    await delay(700);
  }
  if (!allowUserInteraction) {
    throw manualInteractionRequired("Logowanie do Allianz w tle nie powiodło się. Okno Chrome otworzy się do dokończenia logowania.");
  }
  throw new Error("Logowanie do Allianz nie zostało dokończone w ciągu 10 minut.");
}

async function clickVisibleButtonByText(page, pattern) {
  return page.evaluate((source) => {
    const regex = new RegExp(source, "i");
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
    const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
    const button = [...document.querySelectorAll("button, [role='button']")]
      .filter(visible)
      .find((element) => regex.test(compact(element.innerText)));
    if (!button || button.disabled) return false;
    button.click();
    return true;
  }, pattern.source);
}

async function clearAndTypeInto(page, handle, text) {
  await handle.click();
  await page.keyboard.down("Control");
  await page.keyboard.press("KeyA");
  await page.keyboard.up("Control");
  await page.keyboard.press("Backspace");
  await handle.type(String(text), { delay: 30 });
}

 
async function detectOwnerMismatchModal(page) {
  return page.evaluate(() => {
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
    const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
    const text = compact(document.body?.innerText || "");
    if (!/Brak zgodności danych/i.test(text)) return false;
    return [...document.querySelectorAll("button")].filter(visible)
      .some((element) => /PEŁNEJ ŚCIEŻKI/i.test(compact(element.innerText)));
  }).catch(() => false);
}

async function isOnPrequotePage(page) {
  return /\/quick-prequote-page|\/prequote(\?|$)/i.test(page.url());
}

function slugifyAllianzLabel(label) {
  return normalizeText(label).replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "pole";
}

 
async function scanEsamochodEmptyFields(page) {
  return page.evaluate(() => {
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
    const visible = (element) => element instanceof HTMLElement
      && element.getClientRects().length > 0
      && getComputedStyle(element).visibility !== "hidden";
    const fields = [];

    const wrapperOf = (element) => element.closest("nx-formfield, .form-group, [class*='form-field']") || element.parentElement;
    const labelFor = (element) => compact(wrapperOf(element)?.querySelector("label, nx-formfield-label")?.innerText || "");
    
    
    const errorFor = (element) => {
      const wrapper = wrapperOf(element);
      if (!wrapper) return "";
      const errorNode = [...wrapper.querySelectorAll("nx-error, .nx-error, [class*='error' i], [role='alert']")]
        .find((candidate) => visible(candidate) && compact(candidate.innerText));
      return errorNode ? compact(errorNode.innerText).slice(0, 160) : "";
    };

    for (const input of [...document.querySelectorAll("input")].filter(visible)) {
      if (input.type !== "text" && input.type !== "number") continue;
      const value = compact(input.value);
      const error = errorFor(input);
      if (value && !error) continue;
      const label = labelFor(input);
      if (!label) continue;
      fields.push({ id: input.name || "", kind: "text", label, currentValue: value, invalid: Boolean(error), error, required: true, options: [] });
    }

    for (const dropdown of [...document.querySelectorAll("nx-dropdown")].filter(visible)) {
      const value = compact(dropdown.textContent);
      const error = errorFor(dropdown);
      if (value && !error) continue;
      const label = labelFor(dropdown);
      if (!label) continue;
      fields.push({ id: "", kind: "select", label, currentValue: value, invalid: Boolean(error), error, required: true, options: [] });
    }

    
    
    const radioWrapper = (radio) => radio.closest("nx-radio-toggle-button, label") || radio.parentElement;
    const radiosByGroup = new Map();
    for (const radio of [...document.querySelectorAll("input[type='radio']")]) {
      const group = radio.name || "";
      if (!group) continue;
      
      
      const optionWrapper = radioWrapper(radio);
      if (!optionWrapper || !visible(optionWrapper)) continue;
      if (!radiosByGroup.has(group)) radiosByGroup.set(group, []);
      radiosByGroup.get(group).push(radio);
    }
    for (const [group, radios] of radiosByGroup) {
      if (radios.some((radio) => radio.checked)) continue;
      const optionLabels = radios
        .map((radio) => compact(radioWrapper(radio)?.innerText || ""))
        .filter(Boolean);
      if (optionLabels.length === 0) continue;
      
      
      
      let questionText = "";
      let container = radios[0].closest("nx-radio-toggle") || radioWrapper(radios[0])?.parentElement;
      for (let depth = 0; depth < 3 && container && !questionText; depth += 1) {
        let sibling = container.previousElementSibling;
        for (let hops = 0; hops < 4 && sibling; hops += 1) {
          const text = compact(sibling.innerText);
          if (text && text.length <= 200) {
            questionText = text.includes("?") ? `${text.split("?")[0].trim()}?` : text;
            break;
          }
          sibling = sibling.previousElementSibling;
        }
        container = container.parentElement;
      }
      if (!questionText) questionText = `Wybierz: ${optionLabels.join(" / ")}`;
      fields.push({
        id: `radio:${group}`,
        kind: "radio",
        label: questionText.slice(0, 160),
        currentValue: "",
        required: true,
        options: optionLabels.map((text) => ({ value: text, text })),
      });
    }
    return fields;
  }).catch(() => []);
}

async function setAllianzTextInputByName(page, name, value) {
  const handle = await page.$(`input[name='${name}']`);
  if (!handle) return false;
  try {
    await clearAndTypeInto(page, handle, value);
    await page.keyboard.press("Tab").catch(() => {});
    return true;
  } finally {
    await handle.dispose().catch(() => {});
  }
}

async function chooseAllianzRadioOption(page, groupName, wantedText) {
  return page.evaluate(({ group, wanted }) => {
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim().toLowerCase();
    const radios = [...document.querySelectorAll(`input[type='radio'][name='${group}']`)];
    const target = radios.find((radio) => {
      const wrapper = radio.closest("nx-radio-toggle-button, label") || radio.parentElement;
      const label = compact(wrapper?.innerText || "");
      return label === compact(wanted) || label.includes(compact(wanted));
    });
    if (!target) return false;
    const wrapper = target.closest("nx-radio-toggle-button, label") || target;
    wrapper.click();
    if (!target.checked) target.click();
    return true;
  }, { group: groupName, wanted: String(wantedText) }).catch(() => false);
}

 
async function readSuggestedStartDate(page) {
  return page.evaluate(() => {
    const match = String(document.body?.innerText || "").match(/Sugerowany start ochrony to:\s*(\d{2}\/\d{2}\/\d{4})/i);
    return match ? match[1] : "";
  }).catch(() => "");
}

function formatAllianzSlashDate(date) {
  return `${String(date.getDate()).padStart(2, "0")}/${String(date.getMonth() + 1).padStart(2, "0")}/${date.getFullYear()}`;
}

 
async function resolveAllianzStartDate(page) {
  const suggested = await readSuggestedStartDate(page);
  const match = suggested.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
  if (match) {
    const suggestedDate = new Date(Number(match[3]), Number(match[2]) - 1, Number(match[1]));
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const daysAhead = Math.round((suggestedDate.getTime() - today.getTime()) / 86400000);
    if (daysAhead >= 0 && daysAhead <= 30) return suggested;
  }
  const tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  return formatAllianzSlashDate(tomorrow);
}

async function findAllianzInputHandleByLabel(page, label) {
  const handle = await page.evaluateHandle((wanted) => {
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
    const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
    for (const input of [...document.querySelectorAll("input")].filter(visible)) {
      if (input.type !== "text" && input.type !== "number") continue;
      const wrapper = input.closest("nx-formfield, .form-group, [class*='form-field']") || input.parentElement;
      const wrapperLabel = compact(wrapper?.querySelector("label, nx-formfield-label")?.innerText || "");
      if (wrapperLabel.includes(wanted)) return input;
    }
    return null;
  }, label).catch(() => null);
  const element = handle?.asElement?.() || null;
  if (!element) await handle?.dispose?.().catch(() => {});
  return element;
}



function normalizeAllianzAnswerValue(value) {
  const text = String(value || "").trim();
  const dotted = text.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/);
  if (dotted) return `${dotted[1].padStart(2, "0")}/${dotted[2].padStart(2, "0")}/${dotted[3]}`;
  const iso = text.match(/^(\d{4})-(\d{2})-(\d{2})/);
  if (iso) return `${iso[3]}/${iso[2]}/${iso[1]}`;
  return text;
}

async function applyEsamochodAnswer(page, field, rawValue) {
  const value = normalizeAllianzAnswerValue(rawValue);
  if (field.kind === "radio") {
    return chooseAllianzRadioOption(page, String(field.id || "").replace(/^radio:/, ""), value);
  }
  if (field.kind === "select") {
    return chooseAllianzDropdownOption(page, field.label, value);
  }
  if (field.id && !field.id.includes(":")) {
    if (await setAllianzTextInputByName(page, field.id, value)) return true;
  }
  const handle = await findAllianzInputHandleByLabel(page, field.label);
  if (!handle) return false;
  try {
    await clearAndTypeInto(page, handle, value);
    await page.keyboard.press("Tab").catch(() => {});
    return true;
  } finally {
    await handle.dispose().catch(() => {});
  }
}

 
async function runEsamochodFillLoop({ page, knownAnswerResolver, onInputRequested, onProgress }) {
  const askedOnce = new Set();
  
  
  const autoFilledOnce = new Set();
  for (let round = 1; round <= 8; round += 1) {
    const premiumsEarly = await extractAllianzPremiums(page).catch(() => []);
    if (premiumsEarly.length > 0) return premiumsEarly;
    if (page.isClosed?.()) throw new Error("Okno kalkulatora Allianz zostało zamknięte przed odczytaniem składek.");
    if (!(await isOnPrequotePage(page))) {
      await delay(2000);
      const premiums = await extractAllianzPremiums(page).catch(() => []);
      if (premiums.length > 0) return premiums;
      if (!(await isOnPrequotePage(page))) continue;
    }
    const fields = await scanEsamochodEmptyFields(page);
    for (const field of fields) {
      if (field.kind === "select") {
        const options = await listAllianzDropdownOptions(page, field.label);
        field.options = options.map((text) => ({ value: text, text }));
      }
      if (!field.id) field.id = `${field.kind}:${slugifyAllianzLabel(field.label)}`;
    }
    const unresolved = [];
    for (const field of fields) {
      if (autoFilledOnce.has(field.id)) {
        unresolved.push(field);
        continue;
      }
      let value = knownAnswerResolver(field) || "";
      if (/start ochrony/i.test(field.label)) value = await resolveAllianzStartDate(page);
      if (!value && field.kind === "select" && field.options.length === 1) value = field.options[0].text;
      if (!value && /liczba miejsc/i.test(field.label)) {
        const versionText = await page.evaluate(() => {
          const compact = (candidate) => String(candidate || "").replace(/\s+/g, " ").trim();
          for (const dropdown of [...document.querySelectorAll("nx-dropdown")]) {
            const wrapper = dropdown.closest("nx-formfield") || dropdown.parentElement;
            const label = compact(wrapper?.querySelector("label, nx-formfield-label")?.innerText || "");
            if (/Wersja/i.test(label)) return compact(dropdown.textContent);
          }
          return "";
        }).catch(() => "");
        const seatsMatch = versionText.match(/(\d{1,2})\s*miejsc/i);
        if (seatsMatch) value = seatsMatch[1];
      }
      if (!value) {
        unresolved.push(field);
        continue;
      }
      await applyEsamochodAnswer(page, field, value);
      autoFilledOnce.add(field.id);
      await delay(400);
    }
    
    
    const toAsk = unresolved.filter((field) => !askedOnce.has(field.id));
    if (toAsk.length > 0) {
      if (typeof onInputRequested !== "function") return null;
      onProgress({
        status: "running",
        title: "Kalkulator Allianz czeka na odpowiedzi…",
        detail: "Uzupełnij brakujące dane w Bazie Polis.",
      });
      const answers = await onInputRequested({ fields: toAsk, round });
      if (!answers || typeof answers !== "object") return null;
      for (const field of toAsk) {
        askedOnce.add(field.id);
        const answer = String(answers[field.id] || "").trim();
        if (!answer) continue;
        await applyEsamochodAnswer(page, field, answer);
        await delay(400);
      }
    }
    await clickVisibleButtonByText(page, /SPRAWDŹ KALKULACJĘ/i);
    await delay(4500);
  }
  return null;
}

async function listAllianzDropdownOptions(page, labelText) {
  const opened = await page.evaluate((label) => {
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
    const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
    const dropdowns = [...document.querySelectorAll("nx-dropdown")].filter(visible);
    for (const dropdown of dropdowns) {
      const wrapper = dropdown.closest("nx-formfield") || dropdown.parentElement;
      const wrapperLabel = compact(wrapper?.querySelector("label, nx-formfield-label")?.innerText || "");
      if (wrapperLabel.includes(label)) {
        dropdown.click();
        return true;
      }
    }
    return false;
  }, labelText).catch(() => false);
  if (!opened) return [];
  await delay(600);
  const options = await page.evaluate(() => {
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
    return [...document.querySelectorAll("nx-dropdown-item, [role='option']")]
      .map((element) => compact(element.innerText))
      .filter(Boolean);
  }).catch(() => []);
  await page.keyboard.press("Escape").catch(() => {});
  await delay(300);
  return options;
}

async function chooseAllianzDropdownOption(page, labelText, wantedText) {
  const opened = await page.evaluate((label) => {
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
    const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
    const dropdowns = [...document.querySelectorAll("nx-dropdown")].filter(visible);
    for (const dropdown of dropdowns) {
      const wrapper = dropdown.closest("nx-formfield") || dropdown.parentElement;
      const wrapperLabel = compact(wrapper?.querySelector("label, nx-formfield-label")?.innerText || "");
      if (wrapperLabel.includes(label)) {
        dropdown.click();
        return true;
      }
    }
    return false;
  }, labelText).catch(() => false);
  if (!opened) return false;
  await delay(600);
  const picked = await page.evaluate((wanted) => {
    const compact = (value) => String(value || "").replace(/\s+/g, " ").trim().toLowerCase();
    const options = [...document.querySelectorAll("nx-dropdown-item, [role='option']")];
    const target = options.find((element) => compact(element.innerText) === compact(wanted))
      || options.find((element) => compact(element.innerText).includes(compact(wanted)))
      || options.find((element) => compact(wanted).includes(compact(element.innerText)));
    if (!target) return false;
    target.click();
    return true;
  }, wantedText).catch(() => false);
  if (!picked) await page.keyboard.press("Escape").catch(() => {});
  await delay(500);
  return picked;
}

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

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

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

   
  async function openLoginWindow({ username, password, onProgress = () => {} }) {
    let page = lastVisiblePage;
    if (!page || page.isClosed?.()) {
      page = await openPage(false);
      lastVisiblePage = page;
    }
    page.setDefaultTimeout(12000);
    page.setDefaultNavigationTimeout(60000);
    await bringPageWindowToFront(page).catch(() => {});
    if (!isAllianzPortalUrl(page.url())) {
      onProgress({ status: "running", title: "Otwieranie portalu Allianz…", detail: "" });
      await page.goto(ALLIANZ_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
    }
    try {
      const outcome = await ensureAuthenticated(page, { username, password }, { allowUserInteraction: true, onProgress });
      onProgress({
        status: "completed",
        title: outcome.alreadyLoggedIn ? "Allianz jest już zalogowany" : "Zalogowano do Allianz",
        detail: "Ten komputer pozostaje zapamiętany w profilu przeglądarki.",
      });
      return { loggedIn: true, alreadyLoggedIn: outcome.alreadyLoggedIn === true };
    } catch (error) {
      onProgress({
        status: "failed",
        title: "Logowanie do Allianz nie zostało dokończone",
        detail: "Okno pozostało otwarte - możesz dokończyć logowanie i spróbować ponownie.",
      });
      return { loggedIn: false, timedOut: true, windowClosed: page.isClosed?.() === true, error: String(error?.message || "") };
    }
  }

  async function calculateQuoteAttempt({
    registration,
    pesel,
    username,
    password,
    vehicleInfo,
    onInputRequested,
    extraKnownAnswerResolver,
    onProgress,
    manualStartOnly = false,
    onOverlayAction,
  }, { headless = false } = {}) {
    const page = await openPage(headless);
    page.setDefaultTimeout(12000);
    page.setDefaultNavigationTimeout(60000);
    if (!headless) {
      lastVisiblePage = page;
      await bringPageWindowToFront(page);
      await attachClientInfoOverlay(page, vehicleInfo, { onAction: onOverlayAction }).catch(() => {});
    }
    try {
      onProgress({
        status: "running",
        title: headless ? "Kalkulacja Allianz działa w tle…" : "Otwieranie kalkulatora Allianz…",
        detail: "",
      });
      await page.goto(ALLIANZ_MAJATEK_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
      await ensureAuthenticated(page, { username, password }, { allowUserInteraction: !headless, onProgress });
      if (!page.url().includes("sprzedaz-majatek")) {
        await page.goto(ALLIANZ_MAJATEK_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
      }

      
      onProgress({ status: "running", title: "Otwieranie kalkulatora Mój Samochód…", detail: "" });
      const tileDeadline = Date.now() + 45000;
      let tileClicked = false;
      while (Date.now() < tileDeadline && !tileClicked) {
        tileClicked = await page.evaluate(() => {
          const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
          const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
          const tiles = [...document.querySelectorAll("div, section, li")].filter(visible);
          for (const tile of tiles) {
            if (!/^Samochód\s+Mój Samochód/i.test(compact(tile.innerText))) continue;
            const button = [...tile.querySelectorAll("button")]
              .find((candidate) => /oblicz składkę/i.test(compact(candidate.innerText)));
            if (button) {
              button.click();
              return true;
            }
          }
          return false;
        }).catch(() => false);
        if (!tileClicked) await delay(700);
      }
      if (!tileClicked) {
        throw manualInteractionRequired("Nie znaleziono kafla „Mój Samochód” w portalu Allianz. Sprawdź otwarte okno.");
      }
      const ssoDeadline = Date.now() + 60000;
      while (Date.now() < ssoDeadline && !page.url().includes(ESAMOCHOD_HOST)) {
        await delay(500);
      }
      if (!page.url().includes(ESAMOCHOD_HOST)) {
        throw manualInteractionRequired("Kalkulator e-samochod Allianz nie otworzył się. Sprawdź otwarte okno.");
      }
      await delay(2500);

      
      onProgress({ status: "running", title: "Uzupełnianie danych startowych…", detail: "" });
      const formDeadline = Date.now() + 30000;
      let registrationInput = null;
      while (Date.now() < formDeadline && !registrationInput) {
        registrationInput = await page.$("input[name='registrationNumber']");
        if (!registrationInput) {
          await clickVisibleButtonByText(page, /^Nowa kalkulacja$/);
          await delay(900);
        }
      }
      if (!registrationInput) {
        throw manualInteractionRequired("Formularz startowy kalkulatora Allianz nie otworzył się. Sprawdź otwarte okno.");
      }
      if (manualStartOnly) {
        await registrationInput.dispose().catch(() => {});
        onProgress({
          status: "running",
          title: "Karta Allianz jest gotowa do ręcznego liczenia",
          detail: "Wpisz dane w formularzu samodzielnie.",
        });
        return [];
      }
      const birthDate = peselToAllianzBirthDate(pesel);
      if (!birthDate) throw new Error("Kalkulacja Allianz wymaga poprawnego numeru PESEL (data urodzenia).");
      await clearAndTypeInto(page, registrationInput, String(registration || "").toUpperCase());
      await registrationInput.dispose().catch(() => {});
      await page.evaluate(() => {
        const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
        const radios = [...document.querySelectorAll("input[type='radio']")];
        for (const radio of radios) {
          const wrapper = radio.closest("nx-radio-toggle-button, label") || radio.parentElement;
          if (wrapper && compact(wrapper.innerText) === "Osoba fizyczna") {
            if (!radio.checked) wrapper.click();
            return;
          }
        }
      }).catch(() => {});
      const birthInput = await page.$("input[name='birthDate']");
      if (!birthInput) throw new Error("Nie znaleziono pola daty urodzenia w kalkulatorze Allianz.");
      await clearAndTypeInto(page, birthInput, birthDate);
      await birthInput.dispose().catch(() => {});
      await page.keyboard.press("Tab").catch(() => {});
      await delay(900);
      if (!(await clickVisibleButtonByText(page, /^DALEJ$/))) {
        throw new Error("Przycisk DALEJ w kalkulatorze Allianz pozostał nieaktywny - sprawdź numer rejestracyjny i PESEL.");
      }

      
      const cepikDeadline = Date.now() + 40000;
      let ownerMismatch = false;
      while (Date.now() < cepikDeadline) {
        if (await isOnPrequotePage(page)) break;
        if (await detectOwnerMismatchModal(page)) {
          ownerMismatch = true;
          break;
        }
        await delay(600);
      }
      if (ownerMismatch) {
        
        
        
        onProgress({
          status: "running",
          title: "Dane właściciela różnią się od CEPiK…",
          detail: "Przechodzę na pełną ścieżkę - znane pola uzupełnię, o resztę zapytam w Bazie.",
        });
        await clickVisibleButtonByText(page, /PEŁNEJ ŚCIEŻKI/i);
        const fullPathDeadline = Date.now() + 30000;
        while (Date.now() < fullPathDeadline && !(await isOnPrequotePage(page))) {
          await delay(600);
        }
      }
      if (!(await isOnPrequotePage(page))) {
        throw manualInteractionRequired("Kalkulator Allianz nie przeszedł do danych pojazdu. Sprawdź otwarte okno.");
      }

      
      onProgress({ status: "running", title: "Uzupełnianie danych pojazdu…", detail: "" });
      const knownAnswerResolver = composeKnownAnswerResolvers(
        extraKnownAnswerResolver,
        buildKnownVehicleAnswerResolver({ vehicleInfo, pesel }),
      );
      const premiums = await runEsamochodFillLoop({ page, knownAnswerResolver, onInputRequested, onProgress });
      if (premiums && premiums.length > 0) return premiums;
      if (headless) {
        throw manualInteractionRequired("Kalkulator Allianz wymaga dokończenia w oknie przeglądarki. Użyj „Popraw ręcznie”.");
      }
      onProgress({
        status: "action_required",
        title: "Dokończ kalkulację w oknie Allianz",
        detail: "Uzupełnij pozostałe dane w otwartym oknie - składki zostaną odczytane automatycznie.",
      });
      const manualDeadline = Date.now() + MANUAL_FULL_PATH_TIMEOUT_MS;
      while (Date.now() < manualDeadline) {
        if (page.isClosed?.()) throw new Error("Okno kalkulatora Allianz zostało zamknięte przed odczytaniem składek.");
        const manualPremiums = await extractAllianzPremiums(page).catch(() => []);
        if (manualPremiums.length > 0) return manualPremiums;
        await delay(1500);
      }
      throw new Error("Nie udało się odczytać składek z kalkulatora Allianz.");
    } finally {
      if (headless) await page.close().catch(() => {});
    }
  }

  async function calculateQuote(options) {
    
    
    
    const canRunInBackground = options?.runInBackground === true;
    const visibleBrowserActive = isVisibleBrowserActive();
    if (!canRunInBackground || visibleBrowserActive) return calculateQuoteAttempt(options, { headless: false });
    return calculateQuoteAttempt(options, { headless: true });
  }

  async function importPortfolioAttempt({
    username,
    password,
    sinceDate = "",
    downloadPdfs = false,
    pdfDirectory = "",
    pdfPolicyLimit,
    skipPdfPolicyNumbers = [],
    knownPdfPolicyNumbers = [],
    onCompletion,
    onProgress,
  }, { headless = false } = {}) {
    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 Allianz działa w tle…" : "Otwieranie portalu Allianz…",
        detail: "",
      });
      await page.goto(ALLIANZ_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
      await ensureAuthenticated(page, { username, password }, { allowUserInteraction: !headless, onProgress });

      onProgress({ status: "running", title: "Odczytywanie listy polis…", detail: "" });
      await page.goto(ALLIANZ_POLICY_LIST_PAGE_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
      
      if (!(await isPortalAuthenticated(page))) {
        await ensureAuthenticated(page, { username, password }, { allowUserInteraction: !headless, onProgress });
        await page.goto(ALLIANZ_POLICY_LIST_PAGE_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
      }
      const items = await fetchAllianzPolicies(page, {
        onProgress({ collected, total }) {
          onProgress({
            status: "running",
            title: "Pobieranie polis z Allianz…",
            detail: total ? `${collected} z ${total}` : "",
          });
        },
      });
      await enrichAllianzPolicyItems(page, items, {
        onProgress({ processed, total }) {
          onProgress({
            status: "running",
            title: "Uzupełnianie danych klientów Allianz…",
            detail: total ? `${processed} z ${total}` : "",
          });
        },
      });
      const mappingCollector = createPortfolioMappingCollector(items.length);
      const minEndDate = getAllianzPortfolioMinEndDate();
      const allPolicies = mapAllianzPolicies(items, {
        
        
        minEndDate,
        mappingCollector,
      });
      const mappingReport = assertPortfolioMappingComplete(mappingCollector.createReport());
      let pdfBatch = null;
      if (downloadPdfs) {
        onProgress({
          status: "running",
          title: "Pobieranie plików PDF polis Allianz…",
          detail: "",
        });
        pdfBatch = await runAllianzPortfolioPdfBatch({
          page,
          retryStore: pdfRetryStore,
          pdfDirectory,
          policies: allPolicies,
          limit: pdfPolicyLimit,
          skipPolicyNumbers: skipPdfPolicyNumbers,
          knownPolicyNumbers: knownPdfPolicyNumbers,
          onProgress({ processed, planned }) {
            onProgress({
              status: "running",
              title: "Pobieranie plików PDF polis Allianz…",
              detail: planned ? `${processed} z ${planned}` : "",
            });
          },
        });
      }
      const { emittedPolicies, emissionReport } = createPortfolioEmissionResult(allPolicies, {
        sinceDate,
        getFreshnessDates: (policy) => policy?.issueDate,
      });
      onCompletion?.({
        freshCount: emissionReport.freshCount,
        pdfRetry: pdfBatch?.publicResult || null,
        pdfRetryIdentities: pdfBatch?.privateResult?.manualRetryIdentities || [],
        mappingReport,
        emissionReport,
      });
      return emittedPolicies;
    } finally {
      if (headless) await page.close().catch(() => {});
    }
  }

  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() {
    if (!lastVisiblePage || lastVisiblePage.isClosed?.()) return false;
    return bringPageWindowToFront(lastVisiblePage).then(() => true).catch(() => false);
  }

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

  return { openLoginWindow, importPortfolio, calculateQuote, focusWindow, close };
}
