import puppeteer from "puppeteer-core";
import {
  bringPageWindowToFront,
  buildKnownVehicleAnswerResolver,
  composeKnownAnswerResolvers,
  fillDescribedFields,
  hasDescribedFields,
  learnOrReplayActionPath,
  learnOrReplayFormPath,
} from "./etrzxtbsmrmtpfpyetfs.mjs";
import { attachClientInfoOverlay } from "./hkwnbsimckiekhsfxcto.mjs";
import {
  downloadWartaPolicyPdfBase64,
  fetchWartaAgreements,
  fetchWartaPolicyRows,
  isOnWartaView360,
  mapWartaPolicyRows,
} from "./alrhwsnhpalfkratbtkanadrbpklonylarugwtajftfiqdevwnfluiv.mjs";
import { runPortfolioPdfBatch } from "./ylivitleiyuglkyjgkec.mjs";
import {
  createPortfolioPdfPolicySelectorSet,
  portfolioPdfPolicySelectorMatches,
} from "./abywezhrsiqkqfhujfvq.mjs";
import {
  assertPortfolioMappingComplete,
  createPortfolioEmissionResult,
  createPortfolioMappingCollector,
} from "./fgroazrhswhewnaqxvgl.mjs";

const WARTA_URL = "https://eagent.warta.pl/";
const WARTA_VIEW360_URL = "https://eagent.warta.pl/view360/";
const PORTAL_TIMEOUT_MS = 3 * 60 * 1000;

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

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

export function prepareWartaPortfolioPdfEntries({
  policies,
  skipPolicyNumbers = [],
  knownPolicyNumbers = [],
} = {}) {
  const skipSet = createPortfolioPdfPolicySelectorSet(skipPolicyNumbers, {
    insurer: "warta",
  });
  const knownSet = createPortfolioPdfPolicySelectorSet(knownPolicyNumbers, {
    insurer: "warta",
  });
  const eligible = [];
  for (const policy of Array.isArray(policies) ? policies : []) {
    const policyNumber = String(policy?.policyNumber || "")
      .replace(/[^0-9A-Za-z]/g, "")
      .toUpperCase();
    const identitySource = { ...policy, policyNumber };
    if (
      !policyNumber
      || !policy?.policyCtxId
      || portfolioPdfPolicySelectorMatches(skipSet, identitySource)
    ) continue;
    eligible.push({
      policy,
      policyNumber,
      known: portfolioPdfPolicySelectorMatches(knownSet, identitySource),
    });
  }
  eligible.sort((left, right) => Number(left.known) - Number(right.known));
  return eligible;
}

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

async function getFrameBodyText(frame) {
  try {
    return await frame.evaluate(() => String(document.body?.innerText || "").slice(0, 20000));
  } catch {
    return "";
  }
}

async function getCombinedPageText(page) {
  const parts = [];
  for (const frame of page.frames()) {
    const text = await getFrameBodyText(frame);
    if (text) parts.push(text);
  }
  return parts.join(" ").slice(0, 50000);
}

function normalizeWartaRecordSelection(selection) {
  const id = String(selection?.id || "").trim().toUpperCase().slice(0, 24);
  if (!/^(?:A\d{8,14}|\d{5,12})$/.test(id)) return null;
  return {
    id,
    label: String(selection?.label || id).replace(/\s+/g, " ").trim().slice(0, 240) || id,
  };
}

export function wartaPathIncludesRecordSelection(path) {
  return (Array.isArray(path) ? path : []).some((step) => {
    const text = String(step?.text || "").replace(/\s+/g, " ").trim().toUpperCase();
    if (/^(?:A\d{8,14}|\d{5,12})$/.test(text)) return true;
    return /\b(?:A\d{8,14}|\d{5,12})\b/.test(text)
      && /AGENT_|MAJATEK|MAJĄTEK|ZYCIE|ŻYCIE/.test(text);
  });
}

export async function findWartaRecordCandidates(page) {
  const candidates = [];
  for (const frame of page.frames()) {
    try {
      const frameCandidates = await frame.evaluate(() => {
        const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
        const recordPattern = /\b(?:A\d{8,14}|\d{5,12})\b/gi;
        const clickableSelector = [
          "a",
          "button",
          "[role='button']",
          "[onclick]",
          "[tabindex]:not([tabindex='-1'])",
        ].join(",");
        const isVisible = (element) => element instanceof HTMLElement
          && element.getClientRects().length > 0
          && getComputedStyle(element).visibility !== "hidden";
        const results = [];
        for (const element of document.querySelectorAll(clickableSelector)) {
          if (!isVisible(element)) continue;
          const elementText = compact(element.innerText || element.textContent);
          const ids = [...elementText.matchAll(recordPattern)].map((match) => match[0].toUpperCase());
          if (ids.length === 0) continue;
          for (const id of new Set(ids)) {
            let label = elementText;
            let context = element.parentElement;
            for (let level = 0; level < 4 && context; level += 1, context = context.parentElement) {
              const contextText = compact(context.innerText || context.textContent);
              const contextIds = [...contextText.matchAll(recordPattern)].map((match) => match[0].toUpperCase());
              if (contextText.length <= 320 && contextIds.filter((candidateId) => candidateId === id).length === 1) {
                label = contextText;
                if (/agent_|maj(?:ą|a)tek|życie|zycie/i.test(contextText)) break;
              }
            }
            results.push({ id, label: label.slice(0, 240) || id });
          }
        }
        return results;
      });
      candidates.push(...frameCandidates);
    } catch {
    }
  }
  const candidatesById = new Map();
  for (const candidate of candidates) {
    const normalized = normalizeWartaRecordSelection(candidate);
    if (!normalized) continue;
    const current = candidatesById.get(normalized.id);
    if (!current || normalized.label.length < current.label.length) candidatesById.set(normalized.id, normalized);
  }
  return [...candidatesById.values()];
}

async function frameHasSelector(page, selector) {
  for (const frame of page.frames()) {
    try {
      if (await frame.$(selector)) return true;
    } catch {
    }
  }
  return false;
}

async function detectAuthenticationInterruption(page) {
  const pageText = normalizeText(await getCombinedPageText(page));
  if (pageText.includes("nieprawidlowy login") || pageText.includes("nieprawidlowe haslo") || pageText.includes("bledne dane logowania")) {
    return { type: "error", message: "Warta odrzuciła login lub hasło." };
  }
  if (
    pageText.includes("kod bezpieczenstwa")
    || pageText.includes("kod jednorazowy")
    || pageText.includes("uwierzytelnianie dwuskladnikowe")
    || pageText.includes("token mfa")
  ) {
    return {
      type: "action",
      title: "Potwierdź logowanie do Warty",
      detail: "Wpisz kod potwierdzający w otwartym oknie.",
    };
  }
  if (pageText.includes("captcha") || await frameHasSelector(page, "iframe[src*='captcha'], [class*='captcha']")) {
    return {
      type: "action",
      title: "Warta wymaga potwierdzenia CAPTCHA",
      detail: "Rozwiąż CAPTCHA w otwartym oknie.",
    };
  }
  return null;
}

async function loginIfNeeded(page, credentials, onProgress) {
  const startedAt = Date.now();
  let loginFormReady = false;
  while (Date.now() - startedAt < 60000) {
    if (await frameHasSelector(page, "#username") && await frameHasSelector(page, "#password")) {
      loginFormReady = true;
      break;
    }
    if (new URL(page.url()).hostname === "eagent.warta.pl") {
      const pageText = normalizeText(await getCombinedPageText(page));
      if (pageText.includes("nowa sprzedaz")) return;
    }
    if ((await findWartaRecordCandidates(page)).length > 0) return;
    await delay(500);
  }
  if (!loginFormReady) throw new Error("Nie udało się otworzyć formularza logowania Warty.");

  let submitted = false;
  for (const frame of page.frames()) {
    try {
      const username = await frame.$("#username");
      const password = await frame.$("#password");
      const submit = await frame.$("#submitBtn");
      if (!username || !password || !submit) continue;
      await username.click({ count: 3 });
      await username.type(credentials.username, { delay: 18 });
      await password.click({ count: 3 });
      await password.type(credentials.password, { delay: 18 });
      onProgress({
        status: "running",
        title: "Logowanie do Warty…",
        detail: "",
      });
      await submit.click();
      submitted = true;
      break;
    } catch {
    }
  }
  if (!submitted) throw new Error("Nie udało się obsłużyć formularza logowania Warty.");
}

async function waitForPortal(page, onProgress, {
  targetFields,
  allowUserInteraction = true,
  promptForRecordChoice = false,
} = {}) {
  const startedAt = Date.now();
  const portalTimeout = allowUserInteraction ? PORTAL_TIMEOUT_MS : Math.min(PORTAL_TIMEOUT_MS, 45000);
  let lastActionMessage = "";

  while (Date.now() - startedAt < portalTimeout) {
    const interruption = await detectAuthenticationInterruption(page);
    if (interruption?.type === "error") throw new Error(interruption.message);
    if (interruption?.type === "action" && !allowUserInteraction) {
      throw backgroundInteractionRequired(`${interruption.title}. Potrzebne jest widoczne okno Chrome.`);
    }
    if (interruption?.type === "action" && interruption.title !== lastActionMessage) {
      lastActionMessage = interruption.title;
      onProgress({ status: "action_required", title: interruption.title, detail: interruption.detail });
    }
    const pageText = normalizeText(await getCombinedPageText(page));
    if (targetFields && await hasDescribedFields(page, targetFields)) {
      return;
    }
    if (new URL(page.url()).hostname === "eagent.warta.pl") {
      if (pageText.includes("nowa sprzedaz")) {
        return;
      }
    }
    const recordCandidates = await findWartaRecordCandidates(page);
    const recordChooserVisible = recordCandidates.length > 0
      && (recordCandidates.length > 1 || pageText.includes("agent_") || pageText.includes("majatek") || pageText.includes("zycie"));
    if (recordChooserVisible) {
      if (allowUserInteraction && promptForRecordChoice) {
        onProgress({
          status: "action_required",
          title: "Wybierz właściwy rekord Warty",
          detail: "Podczas nagrywania kliknij właściwy rekord - zostanie zapamiętany.",
        });
      }
      return;
    }
    await delay(800);
  }
  if (!allowUserInteraction) {
    throw backgroundInteractionRequired("Panel Warty nie zakończył logowania lub wyboru rekordu w trybie tła.");
  }
  throw new Error("Panel Warta eAgent nie otworzył się w ciągu 3 minut.");
}



async function ensureWartaView360Session(page, credentials, onProgress, { allowUserInteraction = true } = {}) {
  await page.goto(WARTA_VIEW360_URL, { waitUntil: "domcontentloaded", timeout: 60000 }).catch(() => {});
  await delay(1200);
  if (isOnWartaView360(page)) {
    try {
      const agreements = await fetchWartaAgreements(page);
      if (agreements.length > 0) return agreements;
    } catch {
    }
  }

  await loginIfNeeded(page, credentials, onProgress);

  const startedAt = Date.now();
  const timeout = allowUserInteraction ? PORTAL_TIMEOUT_MS : Math.min(PORTAL_TIMEOUT_MS, 60000);
  let lastActionTitle = "";
  let redirectedToView360 = false;
  while (Date.now() - startedAt < timeout) {
    const interruption = await detectAuthenticationInterruption(page);
    if (interruption?.type === "error") throw new Error(interruption.message);
    if (interruption?.type === "action") {
      if (!allowUserInteraction) {
        throw backgroundInteractionRequired(`${interruption.title}. Potrzebne jest widoczne okno Chrome.`);
      }
      if (interruption.title !== lastActionTitle) {
        lastActionTitle = interruption.title;
        onProgress({ status: "action_required", title: interruption.title, detail: interruption.detail });
      }
    }
    if (isOnWartaView360(page)) {
      try {
        const agreements = await fetchWartaAgreements(page);
        if (agreements.length > 0) return agreements;
      } catch {
      }
    } else if (!redirectedToView360 && new URL(page.url()).hostname === "eagent.warta.pl") {
      redirectedToView360 = true;
      await page.goto(WARTA_VIEW360_URL, { waitUntil: "domcontentloaded", timeout: 60000 }).catch(() => {});
    }
    await delay(700);
  }
  throw new Error("Nie udało się otworzyć modułu view360 Warty (lista rekordów) w wyznaczonym czasie.");
}

export async function clickWartaControlByText(page, expectedTexts, timeout = 30000) {
  const normalizedExpectedTexts = expectedTexts.map(normalizeText).filter(Boolean);
  const deadline = Date.now() + timeout;
  while (Date.now() < deadline) {
    for (const frame of page.frames()) {
      let candidates = [];
      try {
        candidates = await frame.$$("button, a, [role='button'], input[type='button'], input[type='submit']");
        for (const candidate of candidates) {
          const state = await candidate.evaluate((element) => ({
            text: String(element.innerText || element.value || element.textContent || "")
              .normalize("NFD")
              .replace(/[\u0300-\u036f]/g, "")
              .replace(/\s+/g, " ")
              .trim()
              .toLowerCase(),
            visible: element instanceof HTMLElement
              && element.getClientRects().length > 0
              && getComputedStyle(element).visibility !== "hidden",
            disabled: Boolean(element.disabled) || element.getAttribute("aria-disabled") === "true",
          }));
          if (!state.visible || state.disabled || !normalizedExpectedTexts.includes(state.text)) continue;
          await candidate.click();
          return true;
        }
      } catch {
      } finally {
        await Promise.all(candidates.map((candidate) => candidate.dispose().catch(() => {})));
      }
    }
    await delay(350);
  }
  return false;
}

export async function clickWartaControlBySelector(page, selector, timeout = 15000) {
  const deadline = Date.now() + timeout;
  while (Date.now() < deadline) {
    for (const frame of page.frames()) {
      let candidates = [];
      try {
        candidates = await frame.$$(selector);
        for (const candidate of candidates) {
          const state = await candidate.evaluate((element) => ({
            visible: element instanceof HTMLElement
              && element.getClientRects().length > 0
              && getComputedStyle(element).visibility !== "hidden",
            disabled: Boolean(element.disabled) || element.getAttribute("aria-disabled") === "true",
          }));
          if (!state.visible || state.disabled) continue;
          await candidate.click();
          return true;
        }
      } catch {
      } finally {
        await Promise.all(candidates.map((candidate) => candidate.dispose().catch(() => {})));
      }
    }
    await delay(300);
  }
  return false;
}

const WARTA_FAST_INPUT_CHECK_SELECTOR = "#fast-input-check, button[data-dtname='calculation-fast-input-check']";

function isWartaFastInputRequest(request) {
  let resourceType = "";
  let url = "";
  try {
    resourceType = request.resourceType();
    url = request.url().toLowerCase();
  } catch {
    return false;
  }
  if (resourceType !== "xhr" && resourceType !== "fetch") return false;
  return !url.includes("googletagmanager")
    && !url.includes("google-analytics")
    && !url.includes("doubleclick")
    && !url.includes("dynatrace");
}

async function getWartaFastInputUiState(page) {
  const state = {
    buttonVisible: false,
    buttonDisabled: false,
    busy: false,
  };
  for (const frame of page.frames()) {
    try {
      const frameState = await frame.evaluate((selector) => {
        const isVisible = (element) => element instanceof HTMLElement
          && element.getClientRects().length > 0
          && getComputedStyle(element).visibility !== "hidden";
        const button = [...document.querySelectorAll(selector)].find(isVisible);
        const fastInputContainer = button?.closest(".widget-tile, [section='moto-fast-input'], dynamic-fields") || document;
        const busy = [...fastInputContainer.querySelectorAll(
          "[aria-busy='true'], [class*='loader'], [class*='loading'], [class*='spinner'], .fa-spinner",
        )].some(isVisible);
        return {
          buttonVisible: Boolean(button),
          buttonDisabled: Boolean(button?.disabled)
            || button?.getAttribute("aria-disabled") === "true"
            || button?.classList.contains("disabled") === true,
          busy,
        };
      }, WARTA_FAST_INPUT_CHECK_SELECTOR);
      state.buttonVisible ||= frameState.buttonVisible;
      state.buttonDisabled ||= frameState.buttonDisabled;
      state.busy ||= frameState.busy;
    } catch {
    }
  }
  return state;
}

async function clickWartaFastInputCheck(page, timeout = 15000) {
  const deadline = Date.now() + timeout;
  while (Date.now() < deadline) {
    for (const frame of page.frames()) {
      try {
        const clicked = await frame.evaluate((selector) => {
          const button = [...document.querySelectorAll(selector)].find((element) => (
            element instanceof HTMLElement
            && element.getClientRects().length > 0
            && getComputedStyle(element).visibility !== "hidden"
            && !element.disabled
            && element.getAttribute("aria-disabled") !== "true"
          ));
          if (!button) return false;
          button.scrollIntoView({ block: "center", inline: "center" });
          button.focus({ preventScroll: true });
          button.click();
          return true;
        }, WARTA_FAST_INPUT_CHECK_SELECTOR);
        if (clicked) return true;
      } catch {
      }
    }
    await delay(300);
  }
  return false;
}

async function clickWartaFastInputCheckAndWait(page, timeout = 45000) {
  const trackedRequests = new Set();
  let requestStarted = false;
  let requestCompleted = false;
  const onRequest = (request) => {
    if (!isWartaFastInputRequest(request)) return;
    requestStarted = true;
    trackedRequests.add(request);
  };
  const onRequestDone = (request) => {
    if (!trackedRequests.delete(request)) return;
    requestCompleted = true;
  };
  page.on("request", onRequest);
  page.on("requestfinished", onRequestDone);
  page.on("requestfailed", onRequestDone);

  try {
    await delay(700);
    if (!(await clickWartaFastInputCheck(page, Math.min(timeout, 15000)))) return false;

    const deadline = Date.now() + timeout;
    let processingObserved = false;
    while (Date.now() < deadline) {
      const state = await getWartaFastInputUiState(page);
      processingObserved ||= requestStarted
        || requestCompleted
        || state.buttonDisabled
        || state.busy
        || !state.buttonVisible;

      if (processingObserved && trackedRequests.size === 0 && !state.busy) {
        await page.waitForNetworkIdle({ idleTime: 1000, timeout: 8000 }).catch(() => {});
        await delay(800);
        return true;
      }
      await delay(250);
    }
    return false;
  } finally {
    page.off("request", onRequest);
    page.off("requestfinished", onRequestDone);
    page.off("requestfailed", onRequestDone);
  }
}

async function hasWartaCheckRequiredError(page) {
  for (const frame of page.frames()) {
    try {
      const visible = await frame.evaluate(() => {
        const normalize = (value) => String(value || "")
          .normalize("NFD")
          .replace(/[\u0300-\u036f]/g, "")
          .replace(/\s+/g, " ")
          .trim()
          .toLowerCase();
        return [...document.querySelectorAll("[class*='message-error'], [class*='error-section']")]
          .filter((element) => element instanceof HTMLElement && element.getClientRects().length > 0)
          .some((element) => {
            const text = normalize(element.innerText || element.textContent);
            return text.includes("nacisnij sprawdz") || text.includes("nacisnij przycisk sprawdz");
          });
      });
      if (visible) return true;
    } catch {
    }
  }
  return false;
}

export async function extractPremiums(page) {
  const allPremiums = [];
  for (const frame of page.frames()) {
    try {
      const framePremiums = await frame.evaluate(() => {
        const normalize = (value) => String(value || "")
          .normalize("NFD")
          .replace(/[\u0300-\u036f]/g, "")
          .replace(/\s+/g, " ")
          .trim()
          .toLowerCase();
        const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
        const currencyPattern = /(\d{1,3}(?:[ .]\d{3})*|\d+)(?:,([0-9]{2}))?\s*(?:zł|PLN)/i;
        const findAmount = (value) => compact(value).match(currencyPattern)?.[0]
          ?.replace(/\s+/g, " ")
          .replace(/pln/i, "zł")
          .trim() || "";
        const amountNumber = (value) => {
          const match = compact(value).match(currencyPattern);
          if (!match) return null;
          const whole = Number(match[1].replace(/[ .]/g, ""));
          const fraction = Number(match[2] || "0") / 100;
          return Number.isFinite(whole) ? whole + fraction : null;
        };
        const formatAmount = (value) => {
          if (!Number.isFinite(value)) return "";
          const formatted = new Intl.NumberFormat("pl-PL", {
            minimumFractionDigits: Number.isInteger(value) ? 0 : 2,
            maximumFractionDigits: 2,
            useGrouping: "always",
          }).format(value).replace(/[\u00a0\u202f]/g, " ");
          return `${formatted} zł`;
        };
        const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
        const cellPremium = (cell, allowFallback = true) => {
          const premiumElements = [...cell.querySelectorAll([
            ".variant-tile__body__info__text",
            "[class*='variant-tile__body__info__text']",
            "[class*='premium']",
            "[class*='price']",
            "[class*='sklad']",
            "[data-dtname*='premium']",
            "[data-dtname*='price']",
          ].join(", "))]
            .filter((element) => visible(element) && !element.matches("input, select, option"));
          for (const element of premiumElements) {
            const amount = findAmount(element.textContent);
            if (amount) return amount;
          }
          
          
          if (!allowFallback) return "";
          const fallbackAmounts = [...compact(cell.innerText || cell.textContent).matchAll(
            new RegExp(currencyPattern.source, "gi"),
          )].map((match) => findAmount(match[0])).filter(Boolean);
          return fallbackAmounts.at(-1) || "";
        };
        const cellInsuranceSum = (cell) => {
          const input = [...cell.querySelectorAll("input")].find(visible);
          const select = [...cell.querySelectorAll("select")].find(visible);
          const selectedMarker = cell.querySelector(
            "[aria-selected='true'], [aria-checked='true'], .selected, .active",
          );
          const rawValue = compact(
            input?.value
            || select?.selectedOptions?.[0]?.textContent
            || selectedMarker?.textContent
            || cell.innerText
            || cell.textContent
            || "",
          );
          if (!/\d/.test(rawValue)) return "";
          const digits = rawValue.replace(/[^0-9]/g, "");
          if (!digits) return "";
          const formatted = new Intl.NumberFormat("pl-PL", { useGrouping: "always" })
            .format(Number(digits))
            .replace(/[\u00a0\u202f]/g, " ");
          return `${formatted} zł`;
        };

        const classString = (element) => {
          const raw = element?.className;
          if (typeof raw === "string") return raw;
          if (raw && typeof raw.baseVal === "string") return raw.baseVal;
          return "";
        };
        const cellChecked = (cell) => {
          const checkbox = cell.querySelector("input[type='checkbox'], input[type='radio']");
          if (checkbox) return Boolean(checkbox.checked);
          const ariaTrue = cell.querySelector("[aria-checked='true'], [aria-selected='true']");
          if (ariaTrue) return true;
          const ariaFalse = cell.querySelector("[aria-checked='false'], [aria-selected='false']");
          if (ariaFalse) return false;
          const positiveTokens = ["check", "checked", "tick", "ok", "success", "selected", "active", "enabled", "included"];
          const negativeTokens = ["unchecked", "times", "close", "remove", "cross", "excluded", "unavailable", "disabled", "inactive", "off"];
          const tokenMatches = (token, markersList) => markersList.some((marker) => token === marker
            || token.endsWith(`--${marker}`) || token.endsWith(`-${marker}`));
          const markers = [...cell.querySelectorAll("i, span, svg, use, [class]")].filter(visible);
          let sawNegative = false;
          for (const marker of markers) {
            const tokens = classString(marker).toLowerCase().split(/\s+/).filter(Boolean);
            for (const token of tokens) {
              if (tokenMatches(token, negativeTokens)) {
                sawNegative = true;
                continue;
              }
              if (tokenMatches(token, positiveTokens)) return true;
            }
          }
          const text = compact(cell.innerText || cell.textContent);
          if (/[✓✔☑]/.test(text)) return true;
          if (/[✕✖✗☒]/.test(text) || /^(nie|brak)$/i.test(text)) return false;
          if (/^(tak)$/i.test(text)) return true;
          if (sawNegative) return false;
          return null;
        };
        const stripDropdownNoise = (value) => compact(String(value || "").replace(/brak wynik[óo]w/gi, " "));
        const usableName = (value) => {
          const name = stripDropdownNoise(value);
          if (name && !currencyPattern.test(name) && /[a-ząćęłńóśźż]/i.test(name)) return name.slice(0, 60);
          return "";
        };
        const markedAsSelected = (element) => {
          if (element.getAttribute("aria-checked") === "true"
            || element.getAttribute("aria-selected") === "true"
            || element.getAttribute("aria-pressed") === "true") return true;
          return classString(element).toLowerCase().split(/\s+/).some((token) => {
            if (/^(?:not|un|in|non)-/.test(token)) return false;
            return ["active", "selected", "checked", "chosen"].some((marker) => token === marker
              || token.endsWith(`--${marker}`) || token.endsWith(`-${marker}`));
          });
        };
        const cellVariantName = (cell) => {
          const select = [...cell.querySelectorAll("select")].find(visible);
          if (select?.selectedOptions?.[0]) {
            const selected = usableName(select.selectedOptions[0].textContent);
            if (selected) return selected;
          }
          const marked = [...cell.querySelectorAll("*")]
            .filter((element) => visible(element) && markedAsSelected(element))
            .map((element) => usableName(element.innerText || element.textContent))
            .filter(Boolean)
            .sort((left, right) => left.length - right.length);
          if (marked.length > 0) return marked[0];
          return usableName(cell.innerText || cell.textContent);
        };
        const riskKind = (label) => {
          const value = normalize(label);
          if (/^oc\b|odpowiedzialnosc cywilna/.test(value)) return "oc";
          if (/^ac\b|autocasco|auto casco/.test(value)) return "ac";
          if (/\bnnw\b|nastepstw[a-z]* nieszczesliwych wypadkow/.test(value)) return "nnw";
          if (/assistance|pomoc w drodze/.test(value)) return "assistance";
          if (/\bszyb(?:y|a|owe)?\b|autoszyb/.test(value)) return "szyby";
          return "";
        };

        const results = [];
        const tables = [...document.querySelectorAll("table.variant-tile__body__grid, table[class*='variant-tile__body__grid']")]
          .filter(visible);
        for (const table of tables) {
          const headerCells = [...table.querySelectorAll("thead th")];
          const variants = headerCells.map((cell, headerIndex) => {
            const totalElement = cell.querySelector(".variant-tile__body__grid__group-header__price, [class*='group-header__price']");
            const total = findAmount(totalElement?.textContent || "");
            if (!total) return null;
            const indexElement = cell.querySelector("[class*='group-header__index']");
            const index = compact(indexElement?.textContent || "").replace(/[^A-Za-z0-9]/g, "").slice(0, 4);
            return {
              cellIndex: cell.cellIndex,
              label: index ? `Wariant ${index.toUpperCase()}` : `Wariant ${headerIndex + 1}`,
              amount: total,
              components: {
                oc: "",
                ac: "",
                acVariant: "",
                vehicleValue: "",
                assistance: "",
                nnw: "",
                szyby: "",
                glassValue: "",
              },
              nnwValues: [],
              nnwIncluded: false,
              acAso: null,
              acRepair: "",
              acScope: [],
              assistanceVariant: "",
              assistanceOptions: [],
            };
          }).filter(Boolean);
          if (variants.length === 0) continue;

          let currentGroup = "";
          const rows = [...table.querySelectorAll("tbody tr")].filter(visible);
          for (const row of rows) {
            const cells = [...row.cells];
            if (cells.length < 2) continue;
            const rowLabel = compact(cells[0]?.innerText || cells[0]?.textContent || "");
            const normalizedLabel = normalize(rowLabel);
            const rowRisk = riskKind(rowLabel);
            if (rowRisk) currentGroup = rowRisk;

            for (const variant of variants) {
              const cell = cells[variant.cellIndex];
              if (!cell) continue;
              const premium = rowRisk
                ? cellPremium(cell, !/suma|limit|franszyza|udzial wlasny|zni[zż]k/.test(normalizedLabel))
                : "";
              const includedWithoutSeparatePrice = Boolean(rowRisk && !premium && cellChecked(cell) === true);
              if (rowRisk === "oc" && premium) variant.components.oc = premium;
              if (rowRisk === "ac" && premium) variant.components.ac = premium;
              if (rowRisk === "nnw" && premium) variant.nnwValues.push(premium);
              if (rowRisk === "assistance" && premium) variant.components.assistance = premium;
              if (rowRisk === "szyby" && premium) variant.components.szyby = premium;
              if (rowRisk === "ac" && includedWithoutSeparatePrice) variant.components.ac ||= "w cenie";
              if (rowRisk === "nnw" && includedWithoutSeparatePrice) variant.nnwIncluded = true;
              if (rowRisk === "assistance" && includedWithoutSeparatePrice) {
                variant.components.assistance ||= "w cenie";
              }
              if (rowRisk === "szyby" && includedWithoutSeparatePrice) variant.components.szyby ||= "w cenie";
              if (currentGroup === "ac" && normalizedLabel.includes("suma ubezpieczenia")) {
                variant.components.vehicleValue ||= cellInsuranceSum(cell);
              }
              if (currentGroup === "szyby" && normalizedLabel.includes("suma ubezpieczenia")) {
                variant.components.glassValue ||= cellInsuranceSum(cell);
              }
              if (currentGroup === "ac" && normalizedLabel && rowRisk !== "ac"
                && !normalizedLabel.includes("suma ubezpieczenia") && !premium) {
                const checked = cellChecked(cell);
                if (normalizedLabel.includes("aso") || normalizedLabel.includes("napraw")) {
                  const repairName = cellVariantName(cell);
                  if (repairName) variant.acRepair ||= repairName;
                  else if (checked !== null) variant.acAso = checked;
                } else if (checked === true) {
                  variant.acScope.push(rowLabel);
                }
              }
              if (currentGroup === "assistance" && normalizedLabel && rowRisk !== "assistance" && !premium) {
                if (normalizedLabel.includes("wariant") || normalizedLabel.includes("pakiet") || normalizedLabel.includes("zakres")) {
                  const name = cellVariantName(cell);
                  if (name) variant.assistanceVariant ||= name;
                } else if (cellChecked(cell) === true) {
                  variant.assistanceOptions.push(rowLabel);
                }
              }
            }
          }

          const anyAsoChecked = variants.some((candidate) => candidate.acAso === true);
          for (const variant of variants) {
            const nnwTotal = variant.nnwValues
              .map(amountNumber)
              .filter(Number.isFinite)
              .reduce((sum, amount) => sum + amount, 0);
            variant.components.nnw = nnwTotal > 0
              ? formatAmount(nnwTotal)
              : variant.nnwIncluded
                ? "w cenie"
                : "";
            delete variant.nnwValues;
            delete variant.nnwIncluded;

            if (anyAsoChecked && variant.acAso === null && !variant.acRepair) variant.acAso = false;
            let acNote = "";
            if (variant.acRepair) acNote = `Naprawa: ${variant.acRepair}`;
            else if (variant.acAso === true) acNote = "Naprawa w ASO";
            else if (variant.acAso === false) acNote = "bez naprawy w ASO";
            else if (variant.acScope.length > 0) acNote = variant.acScope.slice(0, 2).join(", ");
            if (variant.components.ac && acNote) variant.components.acVariant = acNote;
            delete variant.acAso;
            delete variant.acRepair;
            delete variant.acScope;

            const assistanceNote = variant.assistanceVariant
              || (variant.assistanceOptions.length > 0 ? variant.assistanceOptions.slice(0, 2).join(", ") : "");
            if (variant.components.assistance && assistanceNote
              && currencyPattern.test(variant.components.assistance)) {
              variant.components.assistance = `${variant.components.assistance} (${assistanceNote})`;
            }
            delete variant.assistanceVariant;
            delete variant.assistanceOptions;

            const details = [
              variant.components.oc && `OC ${variant.components.oc}`,
              variant.components.ac && `AC ${variant.components.ac}`,
              variant.components.vehicleValue && `Wartość pojazdu ${variant.components.vehicleValue}`,
              variant.components.assistance && `Assistance ${variant.components.assistance}`,
              variant.components.nnw && `NNW ${variant.components.nnw}`,
              variant.components.szyby && `Szyby ${variant.components.szyby}`,
            ].filter(Boolean).join(" • ");
            const componentsList = [
              variant.components.oc && { label: "OC", amount: variant.components.oc },
              variant.components.ac && {
                label: variant.components.vehicleValue
                  ? `AC SU: ${variant.components.vehicleValue}`
                  : "AC",
                amount: variant.components.ac,
              },
              variant.components.nnw && { label: "NNW", amount: variant.components.nnw },
              variant.components.assistance && {
                label: assistanceNote ? `Assistance (${assistanceNote})` : "Assistance",
                amount: variant.components.assistance,
              },
              variant.components.szyby && {
                label: variant.components.glassValue
                  ? `Szyby SU: ${variant.components.glassValue}`
                  : "Szyby",
                amount: variant.components.szyby,
              },
            ].filter(Boolean);
            delete variant.components.glassValue;
            const { cellIndex, ...publicVariant } = variant;
            results.push({
              ...publicVariant,
              componentsList,
              details: details || "Warta - kalkulacja komunikacyjna",
            });
          }
        }
        return results;
      });
      allPremiums.push(...framePremiums);
    } catch {
    }
  }
  const uniquePremiums = [];
  const seen = new Set();
  for (const premium of allPremiums) {
    const key = `${normalizeText(premium.label)}|${premium.amount}`;
    if (seen.has(key)) continue;
    seen.add(key);
    uniquePremiums.push(premium);
  }
  return uniquePremiums.slice(0, 12);
}

export async function collectWartaVariantDebug(page) {
  const frames = [];
  for (const frame of page.frames().slice(0, 20)) {
    try {
      const frameRows = await frame.evaluate(() => {
        const compact = (value) => String(value || "").replace(/\s+/g, " ").trim();
        const classString = (element) => {
          const raw = element?.className;
          if (typeof raw === "string") return raw;
          if (raw && typeof raw.baseVal === "string") return raw.baseVal;
          return "";
        };
        const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
        const tables = [...document.querySelectorAll("table.variant-tile__body__grid, table[class*='variant-tile__body__grid']")]
          .filter(visible);
        const rows = [];
        for (const table of tables) {
          for (const row of [...table.querySelectorAll("tbody tr")].filter(visible)) {
            const cells = [...row.cells];
            if (cells.length < 2) continue;
            rows.push({
              cells: cells.slice(1, 5).map((cell) => ({
                hasCheckbox: Boolean(cell.querySelector("input[type='checkbox'], input[type='radio']")),
                checkboxChecked: Boolean(cell.querySelector("input:checked")),
                hasSelect: Boolean(cell.querySelector("select")),
                markerClasses: [...cell.querySelectorAll("i, span, svg")]
                  .filter(visible)
                  .map((marker) => classString(marker))
                  .filter(Boolean)
                  .slice(0, 4),
              })),
            });
          }
        }
        return rows.slice(0, 40);
      });
      if (frameRows.length > 0) frames.push({ rows: frameRows });
    } catch {
    }
  }
  return {
    capturedAt: new Date().toISOString(),
    reason: "warta-warianty-ac-assistance",
    frames,
  };
}

const WARTA_NEXT_SELECTOR = "#footer-button-show-next, button[data-dtname='calculation-footer-button-show-next']";

async function waitForStableWartaNextButton(page, timeout = 45000) {
  const deadline = Date.now() + timeout;
  let consecutiveReadyChecks = 0;
  while (Date.now() < deadline) {
    let ready = false;
    for (const frame of page.frames()) {
      try {
        ready = await frame.evaluate((selector) => {
          const button = [...document.querySelectorAll(selector)].find((element) => (
            element instanceof HTMLElement
            && element.getClientRects().length > 0
            && getComputedStyle(element).visibility !== "hidden"
          ));
          if (!button) return false;
          return !button.disabled
            && button.getAttribute("aria-disabled") !== "true"
            && !button.classList.contains("disabled");
        }, WARTA_NEXT_SELECTOR);
      } catch {
        ready = false;
      }
      if (ready) break;
    }
    consecutiveReadyChecks = ready ? consecutiveReadyChecks + 1 : 0;
    if (consecutiveReadyChecks >= 3) return true;
    await delay(400);
  }
  return false;
}

export async function clickWartaNextAndConfirmTransition(page, targetFields, timeout = 45000) {
  const deadline = Date.now() + timeout;
  let attempts = 0;
  while (Date.now() < deadline && attempts < 3) {
    const readyTimeout = Math.max(0, deadline - Date.now());
    if (!(await waitForStableWartaNextButton(page, readyTimeout))) {
      return { success: false, checkRequired: false, premiums: [] };
    }

    const pageUrlBeforeClick = page.url();
    const clicked = await clickWartaControlBySelector(page, WARTA_NEXT_SELECTOR, 3000);
    if (!clicked) continue;
    attempts += 1;

    
    
    const transitionDeadline = Math.min(deadline, Date.now() + 15000);
    while (Date.now() < transitionDeadline) {
      if (await hasWartaCheckRequiredError(page)) {
        return { success: false, checkRequired: true, premiums: [] };
      }
      const premiums = await extractPremiums(page);
      if (premiums.length > 0) return { success: true, checkRequired: false, premiums };
      if (page.url() !== pageUrlBeforeClick || !(await hasDescribedFields(page, targetFields))) {
        return { success: true, checkRequired: false, premiums: [] };
      }
      await delay(400);
    }
    await delay(900);
  }
  return { success: false, checkRequired: false, premiums: [] };
}

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

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

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

  async function calculateQuoteAttempt({
    registration,
    pesel,
    vin,
    username,
    password,
    vehicleInfo,
    navigationPath,
    formFields,
    resultNavigationPath,
    onPathLearned,
    onDiagnosticsCaptured,
    onResultPathLearned,
    onInputRequested,
    extraKnownAnswerResolver,
    onVehicleDataDiscovered,
    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(() => {});
    }
    
    
    
    let capturedVehicleData = null;
    const wantedRegistration = String(registration || "").replace(/[^A-Z0-9]/gi, "").toUpperCase();
    const onWidgetResponse = (response) => {
      if (capturedVehicleData) return;
      let url = "";
      let resourceType = "";
      try {
        url = response.url();
        resourceType = response.request().resourceType();
      } catch {
        return;
      }
      if (resourceType !== "xhr" && resourceType !== "fetch") return;
      if (!/\/calculation\/widgets\/(state|action)\//i.test(url)) return;
      response.text().then((body) => {
        if (capturedVehicleData || !body || !body.includes("moto-calculation-vehicle")) return;
        try {
          const parsed = JSON.parse(body);
          
          
          const findVehicleWidget = (node, depth = 0) => {
            if (!node || typeof node !== "object" || depth > 4) return null;
            if (node["moto-calculation-vehicle"] && typeof node["moto-calculation-vehicle"] === "object") {
              return node["moto-calculation-vehicle"];
            }
            for (const value of Object.values(node)) {
              const found = findVehicleWidget(value, depth + 1);
              if (found) return found;
            }
            return null;
          };
          const vehicle = findVehicleWidget(parsed);
          if (!vehicle || typeof vehicle !== "object") return;
          const cleanRegistration = String(vehicle.registrationNumber || "").replace(/[^A-Z0-9]/gi, "").toUpperCase();
          if (!cleanRegistration || cleanRegistration !== wantedRegistration) return;
          const firstRegistration = String(vehicle.firstRegDate || "").match(/^(\d{2})-(\d{2})-(\d{4})$/);
          const numberOrEmpty = (value) => (Number.isFinite(value) && value > 0 ? String(value) : "");
          capturedVehicleData = {
            registration: cleanRegistration,
            vin: String(vehicle.vin || "").replace(/[^A-Z0-9]/gi, "").toUpperCase().slice(0, 24),
            firstRegistrationDate: firstRegistration
              ? `${firstRegistration[3]}-${firstRegistration[2]}-${firstRegistration[1]}`
              : "",
            vehicleMake: String(vehicle.ieMake || "").trim().slice(0, 60),
            vehicleModel: String(vehicle.ieModel || "").trim().slice(0, 60),
            productionYear: /^(19|20)\d{2}$/.test(String(vehicle.ieProductionYear || "").trim())
              ? String(vehicle.ieProductionYear).trim()
              : "",
            fuel: vehicle.ieFuel === "E" ? "benzyna" : vehicle.ieFuel === "D" ? "diesel" : "",
            engineCapacity: numberOrEmpty(vehicle.capacity),
            enginePower: numberOrEmpty(vehicle.power),
            grossWeight: numberOrEmpty(vehicle.permGrossWeight),
            seats: numberOrEmpty(vehicle.numberOfSeats),
          };
        } catch {
        }
      }).catch(() => {});
    };
    page.on("response", onWidgetResponse);
    try {
    const targetFields = [
      {
        key: "pesel",
        patterns: [
          "pesel",
          "regon",
          "identity",
          "fast-input-identity",
          "calculation-moto-fast-input-identity",
          "identyfikator klienta",
        ],
        value: pesel,
      },
      {
        key: "registration",
        patterns: [
          "numer rejestracyjny",
          "nr rejestracyjny",
          "nr rej",
          "rejestracj",
          "registrationnumber",
          "fast-input-registration-number",
          "calculation-moto-fast-input-registration-number",
          "registration",
        ],
        value: registration,
      },
    ];
    onProgress({
      status: "running",
      title: headless ? "Kalkulacja Warty działa w tle…" : "Otwieranie kalkulatora Warty…",
      detail: "",
    });
    await page.goto(WARTA_URL, { waitUntil: "domcontentloaded", timeout: 60000 });
    await loginIfNeeded(page, { username, password }, onProgress);
    
    
    const knownAnswerResolver = composeKnownAnswerResolvers(
      extraKnownAnswerResolver,
      buildKnownVehicleAnswerResolver({ vin, vehicleInfo, pesel }),
    );
    const learnFormPath = () => learnOrReplayFormPath({
      page,
      savedPath: navigationPath,
      savedFields: formFields,
      fields: targetFields,
      insurerName: "Warty",
      manualInstruction: "Kliknij „Rozpocznij nagrywanie”, a potem po kolei: właściwy rekord, „Sprzedaż”, Analizę potrzeb Klienta (TAK) i wszystko, co potrzebne, aż zobaczysz pola numeru rejestracyjnego i PESEL. Na końcu użyj zielonego przycisku „Tu jest formularz - zakończ nagrywanie”.",
      requireManualFinish: true,
      onPathLearned,
      onDiagnosticsCaptured,
      onProgress,
      requestInput: onInputRequested,
      knownAnswerResolver,
      allowManual: !headless && !manualStartOnly,
      patientReplay: !manualStartOnly,
    });
    await waitForPortal(page, onProgress, {
      targetFields,
      allowUserInteraction: !headless,
      promptForRecordChoice: !manualStartOnly && !(Array.isArray(navigationPath) && navigationPath.length > 0),
    });
    let formNavigation = null;
    try {
      formNavigation = await learnFormPath();
    } catch (error) {
      if (!manualStartOnly) throw error;
    }
    if (manualStartOnly) {
      onProgress({
        status: "running",
        title: "Karta Warty jest gotowa do ręcznego liczenia",
        detail: "Wpisz dane w formularzu samodzielnie.",
      });
      return [];
    }

    onProgress({
      status: "running",
      title: "Uzupełnianie danych pojazdu i klienta…",
      detail: "",
    });
    if (!(await fillDescribedFields(page, targetFields, formNavigation?.formFields))) {
      throw new Error("Nie znaleziono pól numeru rejestracyjnego i PESEL w kalkulatorze Warty.");
    }

    onProgress({
      status: "running",
      title: "Sprawdzanie pojazdu w Warcie…",
      detail: "",
    });
    let premiums = await extractPremiums(page);
    if (premiums.length === 0) {
      const checkClicked = await clickWartaFastInputCheckAndWait(page, 45000);
      if (!checkClicked) {
        throw new Error("Warta nie rozpoczęła sprawdzania po kliknięciu „SPRAWDŹ”. Przycisk „DALEJ” nie został kliknięty.");
      }
      premiums = await extractPremiums(page);
    }
    if (premiums.length === 0) {
      let nextResult = await clickWartaNextAndConfirmTransition(page, targetFields, 45000);
      if (!nextResult.success && !nextResult.checkRequired) {
        throw new Error("Po sprawdzeniu pojazdu nie pojawił się aktywny przycisk „DALEJ” w kalkulatorze Warty.");
      }
      premiums = nextResult.premiums;
      if (nextResult.checkRequired) {
        onProgress({
          status: "running",
          title: "Ponowne potwierdzenie danych pojazdu…",
          detail: "",
        });
        const checkClicked = await clickWartaFastInputCheckAndWait(page, 45000);
        if (!checkClicked) throw new Error("Warta wymaga kliknięcia „SPRAWDŹ”, ale przycisk nie jest dostępny.");
        nextResult = await clickWartaNextAndConfirmTransition(page, targetFields, 45000);
        premiums = nextResult.premiums;
        if (!nextResult.success) throw new Error("Po zatwierdzeniu danych Warta nie udostępniła przycisku „DALEJ”.");
      }
      const resultsDeadline = Date.now() + 15000;
      while (premiums.length === 0 && Date.now() < resultsDeadline) {
        await delay(500);
        premiums = await extractPremiums(page);
      }
    }

    const areResultsReady = async () => {
      premiums = await extractPremiums(page);
      return premiums.length > 0;
    };
    await learnOrReplayActionPath({
      page,
      savedPath: resultNavigationPath,
      isComplete: areResultsReady,
      requestInput: onInputRequested,
      knownAnswerResolver,
      stageTitle: "od formularza do składek Warty",
      manualInstruction: "Dane są już wpisane. Klikaj dalej tak jak zwykle - również oba przyciski „Dalej” - aż pojawią się składki. Aplikacja zapamięta te czynności.",
      retryInstruction: "Zapamiętana droga do wyników przestała pasować. Klikaj ręcznie aż pojawią się składki, aby zapisać nową sekwencję.",
      onPathLearned: onResultPathLearned,
      onProgress,
      allowManual: !headless,
    });
    premiums = await extractPremiums(page);
    if (premiums.length === 0) throw new Error("Nie udało się odczytać składek z kalkulatora Warty w ciągu 3 minut.");
    const hasVariantNotes = premiums.some((premium) => /\(/.test(String(premium?.components?.ac || ""))
      || /\(/.test(String(premium?.components?.assistance || "")));
    if (!hasVariantNotes) {
      try {
        await onDiagnosticsCaptured?.(await collectWartaVariantDebug(page), "warta-warianty");
      } catch {
      }
    }
    if (capturedVehicleData) {
      try {
        await onVehicleDataDiscovered?.(capturedVehicleData);
      } catch {
      }
    }
    return premiums;
    } finally {
      page.off("response", onWidgetResponse);
      if (headless) await page.close().catch(() => {});
    }
  }

  async function calculateQuote(options) {
    const formPathReady = options?.runInBackground === true
      && Array.isArray(options?.navigationPath)
      && options.navigationPath.length > 0;
    const resultPathReady = Array.isArray(options?.resultNavigationPath)
      && options.resultNavigationPath.length > 0;
    
    
    
    const canRunInBackground = formPathReady && resultPathReady;
    if (formPathReady && !resultPathReady) {
      options?.onProgress?.({
        status: "running",
        title: "Nagranie trasy Warty jest niedokończone…",
        detail: "W otwartym oknie doklikaj do składek. Aplikacja zapisze drogę do wyników i następne kalkulacje pójdą w tle.",
      });
    }
    
    
    const visibleBrowserActive = isVisibleBrowserActive();
    if (!canRunInBackground || visibleBrowserActive) return calculateQuoteAttempt(options, { headless: false });
    return calculateQuoteAttempt(options, { headless: true });
  }

  
  async function importPortfolioRecordsAttempt({ username, password, 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 ? "Pobieranie listy rekordów w tle…" : "Otwieranie portalu Warty…",
        detail: "",
      });
      const agreements = await ensureWartaView360Session(page, { username, password }, onProgress, { allowUserInteraction: !headless });
      onProgress({
        status: "running",
        title: "Wybierz rekord Warty",
        detail: `Znaleziono ${agreements.length} rekordów. Wskaż z listy, z którego pobrać polisy.`,
      });
      return agreements;
    } finally {
      if (headless) await page.close().catch(() => {});
    }
  }

  
  
  async function importPortfolioRecords(options) {
    const startHeadless = isVisibleBrowserActive() ? false : true;
    if (!startHeadless) return importPortfolioRecordsAttempt(options, { headless: false });
    try {
      return await importPortfolioRecordsAttempt(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 importPortfolioRecordsAttempt(options, { headless: false });
    }
  }

  
  
  async function downloadWartaPortfolioPdfs(page, policies, { login, agreementId, pdfDirectory, onProgress, limit = 50, skipPolicyNumbers = [], knownPolicyNumbers = [] }) {
    const eligible = prepareWartaPortfolioPdfEntries({
      policies,
      skipPolicyNumbers,
      knownPolicyNumbers,
    });
    
    
    
    if (eligible.length === 0) {
      onProgress({
        status: "running",
        title: "Pobieranie PDF-ów pominięte",
        detail: "Wszystkie polisy mają już podpięte pliki.",
      });
      return {
        attempted: 0,
        saved: 0,
        remaining: 0,
        pdfRetry: {
          attempted: 0,
          succeeded: 0,
          failed: 0,
          backoff: 0,
          quarantined: 0,
          unsupported: 0,
          deferred: 0,
          remaining: 0,
          batchComplete: true,
          circuitOpen: false,
          circuitCode: null,
          manualRetryAvailable: false,
        },
        pdfRetryIdentities: [],
      };
    }
    const planned = Math.min(eligible.length, limit);
    onProgress({
      status: "running",
      title: "Pobieranie PDF-ów…",
      detail: `Do pobrania w tej partii: ${planned} plików.`,
    });

    let processed = 0;
    let batch;
    try {
      batch = await runPortfolioPdfBatch({
        retryStore: pdfRetryStore,
        insurer: "warta",
        pdfDirectory,
        entries: eligible,
        limit,
        concurrency: 1,
        fetchDocument: async ({ entry }) => {
          const result = await downloadWartaPolicyPdfBase64(page, {
            policyCtxId: entry.policy?.policyCtxId,
            login,
            agreementId,
          });
          processed += 1;
          if (processed % 5 === 0 || processed === planned) {
            onProgress({
              status: "running",
              title: `Pobieranie PDF-ów… (${Math.min(processed, planned)}/${planned})`,
              detail: "",
            });
          }
          await delay(150);
          if (result?.type === "success" && typeof result.base64 === "string") {
            return { type: "success", bytes: Buffer.from(result.base64, "base64") };
          }
          if (
            result?.type === "document_failure"
            || result?.type === "unsupported"
            || result?.type === "global_failure"
          ) {
            return { type: result.type, code: result.code };
          }
          return { type: "global_failure", code: "unknown" };
        },
      });
    } catch {
      batch = {
        publicResult: {
          attempted: processed,
          succeeded: 0,
          failed: 0,
          backoff: 0,
          quarantined: 0,
          unsupported: 0,
          deferred: Math.max(0, eligible.length - processed),
          remaining: eligible.length,
          batchComplete: false,
          circuitOpen: true,
          circuitCode: "store_failed",
          manualRetryAvailable: false,
        },
        privateResult: { manualRetryIdentities: [] },
      };
    }
    const pdfRetry = batch.publicResult;
    
    
    console.error(
      `Warta PDF partia: rozmiar=${processed} attempted=${pdfRetry.attempted} ok=${pdfRetry.succeeded}`
      + ` failed=${pdfRetry.failed} backoff=${pdfRetry.backoff} unsupported=${pdfRetry.unsupported}`
      + ` obwod=${pdfRetry.circuitOpen ? pdfRetry.circuitCode || "unknown" : "-"}`
      + ` pozostalo=${pdfRetry.remaining}`,
    );
    if (processed === 0 && pdfRetry.remaining > 0) {
      onProgress({
        status: "running",
        title: "Pobieranie PDF-ów odłożone",
        detail: pdfRetry.quarantined > 0
          ? "Część dokumentów wymaga ręcznego wznowienia."
          : "Dokumenty oczekują na termin kolejnej bezpiecznej próby.",
      });
    } else {
      onProgress({
        status: "running",
        title: pdfRetry.circuitOpen ? "Pobieranie PDF-ów przerwane" : "PDF-y pobrane",
        detail: pdfRetry.circuitOpen
          ? "Faza dokumentów została bezpiecznie zatrzymana; polisy nadal zostaną zaimportowane."
          : `Zapisano ${pdfRetry.succeeded} z ${pdfRetry.attempted} rozpoczętych plików.`,
      });
    }
    return {
      attempted: pdfRetry.attempted,
      saved: pdfRetry.succeeded,
      remaining: pdfRetry.remaining,
      pdfRetry,
      pdfRetryIdentities: batch.privateResult.manualRetryIdentities,
    };
  }

  
  async function importPortfolioAttempt({ username, password, agreementId, agreementLabel = "", downloadPdfs = false, pdfDirectory = "", pdfPolicyLimit = 50, skipPdfPolicyNumbers = [], knownPdfPolicyNumbers = [], sinceDate = "", onCompletion, onProgress }, { headless = false } = {}) {
    const normalizedAgreementId = String(agreementId || "").trim();
    if (!normalizedAgreementId) throw new Error("Nie wybrano rekordu Warty do importu.");
    let page = !headless && lastVisiblePage && !lastVisiblePage.isClosed() ? lastVisiblePage : null;
    if (!page) {
      page = await openPage(headless);
      page.setDefaultTimeout(12000);
      page.setDefaultNavigationTimeout(60000);
    }
    if (!headless) {
      lastVisiblePage = page;
      await bringPageWindowToFront(page);
    }
    try {
      await ensureWartaView360Session(page, { username, password }, onProgress, { allowUserInteraction: !headless });
      onProgress({
        status: "running",
        title: "Pobieranie polis…",
        detail: agreementLabel ? `Rekord: ${agreementLabel}.` : `Rekord ${normalizedAgreementId}.`,
      });
      
      
      const rows = await fetchWartaPolicyRows(page, normalizedAgreementId, {
        onProgress: ({ collected, total }) => {
          onProgress({
            status: "running",
            title: "Pobieranie polis…",
            detail: `Pobrano ${collected} z ${total} polis.`,
          });
        },
      });
      
      const cutoff = new Date();
      cutoff.setFullYear(cutoff.getFullYear() - 1);
      const minEndDate = `${cutoff.getFullYear()}-${String(cutoff.getMonth() + 1).padStart(2, "0")}-${String(cutoff.getDate()).padStart(2, "0")}`;
      const mappingCollector = createPortfolioMappingCollector(rows.length);
      const allPolicies = mapWartaPolicyRows(rows, {
        minEndDate,
        mappingCollector,
      });
      const mappingReport = assertPortfolioMappingComplete(mappingCollector.createReport());
      const emissionOptions = {
        sinceDate,
        getFreshnessDates: (policy) => [policy?.issueDate, policy?.validFrom],
        isBacklogPolicy: (policy) => policy?.pdfAvailable === true,
      };
      const previewEmission = createPortfolioEmissionResult(allPolicies, emissionOptions);
      if (allPolicies.length === 0) {
        if (sinceDate) {
          onCompletion?.({
            freshCount: previewEmission.emissionReport.freshCount,
            pdfSaved: null,
            pdfRemaining: null,
            mappingReport,
            emissionReport: previewEmission.emissionReport,
          });
          return previewEmission.emittedPolicies;
        }
        throw new Error("Nie znaleziono żadnej polisy do pobrania dla wybranego rekordu.");
      }
      onProgress({
        status: "running",
        title: "Porządkowanie pobranych polis…",
        detail: sinceDate
          ? `Nowych polis: ${previewEmission.emissionReport.freshCount}.`
          : `Pobrano ${allPolicies.length} polis.`,
      });
      let pdfOutcome = null;
      if (downloadPdfs && pdfDirectory) {
        pdfOutcome = await downloadWartaPortfolioPdfs(page, allPolicies, {
          login: username,
          agreementId: normalizedAgreementId,
          pdfDirectory,
          onProgress,
          limit: pdfPolicyLimit,
          skipPolicyNumbers: skipPdfPolicyNumbers,
          knownPolicyNumbers: knownPdfPolicyNumbers,
        });
      }
      const { emittedPolicies, emissionReport } = createPortfolioEmissionResult(
        allPolicies,
        emissionOptions,
      );
      onCompletion?.({
        freshCount: emissionReport.freshCount,
        pdfSaved: pdfOutcome ? pdfOutcome.saved : null,
        pdfRemaining: pdfOutcome ? pdfOutcome.remaining : null,
        pdfRetry: pdfOutcome?.pdfRetry || null,
        pdfRetryIdentities: pdfOutcome?.pdfRetryIdentities || [],
        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() {
    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 { calculateQuote, importPortfolioRecords, importPortfolio, focusWindow, close };
}
