











import { createPortfolioIssueIdentityHash } from "./xombeztlyiegtybxtzkg.mjs";

const PZU_EVEREST_HOST = "everest.pzu.pl";
const PZU_LV_PREFIX = "DesktopPoliciesPzu-DesktopPoliciesScreen-DesktopPoliciesLV";
const PZU_MAX_PAGES = 400; 

function compactText(value) {
  return String(value == null ? "" : value).replace(/\s+/g, " ").trim();
}

function normalizePzuPolicyKey(value) {
  return compactText(value).replace(/[^0-9A-Za-z]/g, "").toLowerCase();
}


export function makePzuPolicyPeriodKey(row) {
  const policyKey = normalizePzuPolicyKey(row?.policyNumber);
  if (!policyKey) return "";
  return [
    policyKey,
    normalizePzuDateYmd(row?.validFrom),
    normalizePzuDateYmd(row?.validUntil),
  ].join("\0");
}

function pzuRowFreshnessKey(row) {
  return [
    normalizePzuDateYmd(row?.validUntil),
    normalizePzuDateYmd(row?.validFrom),
    normalizePzuDateYmd(row?.issueDate),
  ].join("\0");
}

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

export function isOnPzuEverest(page) {
  return getPageHostname(page) === PZU_EVEREST_HOST;
}



async function scrapePzuVisiblePage(page) {
  return page.evaluate((lvPrefix) => {
    const clean = (value) => String(value == null ? "" : value).replace(/\s+/g, " ").trim();
    
    const info = document.querySelector(".gw--paging-info");
    const m = info ? info.textContent.match(/\(\s*(\d+)\s*-\s*(\d+)\s*z\s*(\d+)\s*\)/) : null;
    const rangeFrom = m ? Number(m[1]) : 0;
    const rangeTo = m ? Number(m[2]) : 0;
    const total = m ? Number(m[3]) : 0;

    const rowEls = [...document.querySelectorAll("tr.gw-RowWidget.gw-row")]
      .filter((tr) => tr.id && tr.id.indexOf(lvPrefix) === 0);
    const rows = rowEls.map((tr) => {
      const cell = {};
      for (const el of tr.querySelectorAll("[id]")) {
        const mm = el.id.match(/LV-\d+-([A-Za-z]+)/);
        if (mm && cell[mm[1]] === undefined) cell[mm[1]] = clean(el.textContent);
      }
      if (!cell.PolicyNumber) return null; 
      return {
        policyNumber: cell.PolicyNumber || "",
        businessNumber: cell.BusinessNumber || "",
        clientName: cell.AccountNumber || "",       
        insured: cell.Insured || "",
        regon: cell.Regon || "",
        pesel: cell.PeselPzu || "",
        product: cell.Product || "",
        type: cell.Type || "",
        status: cell.PolicyPeriodStatus || "",
        renewalType: cell.renewdType || "",
        jobNumber: cell.JobNumber || "",
        issueDate: cell.IssueDate || "",
        validFrom: cell.EffectiveDate || "",
        validUntil: cell.ExpirationDate || "",
      };
    }).filter(Boolean);
    return { rows, rangeFrom, rangeTo, total };
  }, PZU_LV_PREFIX);
}


export function parsePzuPagingInfo(text) {
  const m = String(text == null ? "" : text).match(/\(\s*(\d+)\s*-\s*(\d+)\s*z\s*(\d+)\s*\)/);
  if (!m) return null;
  return { rangeFrom: Number(m[1]), rangeTo: Number(m[2]), total: Number(m[3]) };
}







export function assemblePzuPageRows(targets, previousRows, { lvName = "DesktopPoliciesLV" } = {}) {
  const state = new Map();
  for (const [index, row] of (Array.isArray(previousRows) ? previousRows : []).entries()) {
    state.set(String(index), { ...row });
  }
  let pagingText = "";
  const cellRe = new RegExp(lvName + "-(\\d+)-([A-Za-z]+)_Cell");
  for (const target of Array.isArray(targets) ? targets : []) {
    if (!target || typeof target.id !== "string") continue;
    if (Array.isArray(target.fullRows)) {
      
      state.clear();
      for (const [index, row] of target.fullRows.entries()) state.set(String(index), { ...row });
      if (target.pagingText) pagingText = target.pagingText;
      continue;
    }
    if (typeof target.pagingText === "string" && target.pagingText) {
      pagingText = target.pagingText;
      continue;
    }
    const m = target.id.match(cellRe);
    if (!m) continue;
    const row = state.get(m[1]) || {};
    const token = m[2];
    const text = compactText(target.text);
    if (token === "PolicyNumber") row.policyNumber = text;
    else if (token === "BusinessNumber") row.businessNumber = text;
    else if (token === "AccountNumber") row.clientName = text;
    else if (token === "Insured") row.insured = text;
    else if (token === "Regon") row.regon = text;
    else if (token === "PeselPzu") row.pesel = text;
    else if (token === "Product") row.product = text;
    else if (token === "Type") row.type = text;
    else if (token === "PolicyPeriodStatus") row.status = text;
    else if (token === "renewdType") row.renewalType = text;
    else if (token === "JobNumber") row.jobNumber = text;
    else if (token === "IssueDate") row.issueDate = text;
    else if (token === "EffectiveDate") row.validFrom = text;
    else if (token === "ExpirationDate") row.validUntil = text;
    state.set(m[1], row);
  }
  const rows = [...state.entries()]
    .sort((a, b) => Number(a[0]) - Number(b[0]))
    .map(([, row]) => row);
  return { rows, pagingText };
}





async function fetchPzuPagingTargets(page, pageNumber) {
  return page.evaluate(async (lvPrefix, pageNo) => {
    const pagingField = lvPrefix + "-_ListPaging";
    const form = document.querySelector("#gw-root-form");
    if (!form) return { error: "brak #gw-root-form" };
    const fd = new FormData(form);
    const params = new URLSearchParams();
    for (const [key, value] of fd.entries()) {
      if (typeof value === "string") params.append(key, value); 
    }
    params.set("eventSource", pagingField);
    params.set(pagingField, String(pageNo));
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 30000);
    try {
      const response = await fetch(form.action, {
        method: "POST",
        credentials: "include",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: params.toString(),
        signal: controller.signal,
      });
      if (!response.ok) return { error: "HTTP " + response.status };
      const data = await response.json().catch(() => null);
      if (!data || !Array.isArray(data.targets)) return { error: "odpowiedź bez targets" };
      const out = [];
      for (const target of data.targets) {
        if (!target || typeof target.id !== "string" || typeof target.content !== "string") continue;
        if (target.id === lvPrefix) {
          
          const doc = new DOMParser().parseFromString(target.content, "text/html");
          const clean = (v) => String(v == null ? "" : v).replace(/\s+/g, " ").trim();
          const rowEls = [...doc.querySelectorAll("tr")].filter((tr) => tr.id && tr.id.indexOf(lvPrefix) === 0);
          const fullRows = [];
          for (const tr of rowEls) {
            const cell = {};
            for (const el of tr.querySelectorAll("[id]")) {
              const mm = el.id.match(/LV-\d+-([A-Za-z]+)/);
              if (mm && cell[mm[1]] === undefined) cell[mm[1]] = clean(el.textContent);
            }
            if (!cell.PolicyNumber) continue;
            fullRows.push({
              policyNumber: cell.PolicyNumber || "",
              businessNumber: cell.BusinessNumber || "",
              clientName: cell.AccountNumber || "",
              insured: cell.Insured || "",
              regon: cell.Regon || "",
              pesel: cell.PeselPzu || "",
              product: cell.Product || "",
              type: cell.Type || "",
              status: cell.PolicyPeriodStatus || "",
              renewalType: cell.renewdType || "",
              jobNumber: cell.JobNumber || "",
              issueDate: cell.IssueDate || "",
              validFrom: cell.EffectiveDate || "",
              validUntil: cell.ExpirationDate || "",
            });
          }
          const pagingText = doc.querySelector(".gw--paging-info")?.textContent || "";
          out.push({ id: target.id, fullRows, pagingText: clean(pagingText) });
          continue;
        }
        if (target.id.indexOf("_ListPaging") !== -1) {
          const doc = new DOMParser().parseFromString(target.content, "text/html");
          const pagingText = doc.querySelector(".gw--paging-info")?.textContent || doc.body.textContent || "";
          out.push({ id: target.id, pagingText: String(pagingText).replace(/\s+/g, " ").trim() });
          continue;
        }
        if (/LV-\d+-[A-Za-z]+_Cell/.test(target.id)) {
          const doc = new DOMParser().parseFromString(target.content, "text/html");
          out.push({ id: target.id, text: String(doc.body.textContent || "").replace(/\s+/g, " ").trim() });
        }
      }
      return { targets: out };
    } catch (error) {
      return { error: String(error && error.message || error) };
    } finally {
      clearTimeout(timer);
    }
  }, PZU_LV_PREFIX, pageNumber);
}







export async function scrapePzuPolicyRows(page, {
  onProgress = () => {},
  retryDelayMs = 1500,
  pageDelayMs = 150,
} = {}) {
  if (!isOnPzuEverest(page)) {
    throw new Error("Okno PZU nie jest zalogowane. Kliknij „Otwórz okno PZU i zaloguj się”, a potem spróbuj ponownie.");
  }

  
  
  
  
  
  
  const rowByPeriodKey = new Map();
  let rowsSeen = 0;
  const addRows = (rows) => {
    for (const row of rows) {
      const key = makePzuPolicyPeriodKey(row);
      if (!key) continue;
      rowsSeen += 1;
      const existing = rowByPeriodKey.get(key);
      if (!existing || pzuRowFreshnessKey(row) > pzuRowFreshnessKey(existing)) {
        rowByPeriodKey.set(key, row);
      }
    }
  };

  const first = await scrapePzuVisiblePage(page);
  const total = first.total || 0;
  let pageRows = first.rows;
  if (total === 0 && pageRows.length === 0) {
    onProgress({ collected: 0, total: 0, page: 1 });
    return { rows: [], total: 0, sourceCount: 0, duplicateCount: 0 };
  }
  if (first.rangeFrom !== 1 || first.rangeTo < first.rangeFrom || first.rangeTo > total) {
    throw new Error("PZU zwróciło niepełną informację o paginacji pierwszej strony.");
  }
  addRows(pageRows);

  
  
  const pageSize = first.rangeTo - first.rangeFrom + 1;
  const expectedFirstCount = Math.min(pageSize, total);
  if (pageRows.length !== expectedFirstCount || rowsSeen !== expectedFirstCount) {
    throw new Error(`PZU zwróciło niepełną pierwszą stronę (${rowsSeen}/${expectedFirstCount} polis).`);
  }
  onProgress({ collected: rowsSeen, total, page: 1 });
  const pageCount = Math.ceil(total / pageSize);
  if (pageCount > PZU_MAX_PAGES) {
    
    throw new Error(`Lista polis ma ${pageCount} stron - więcej niż obsługiwany limit ${PZU_MAX_PAGES}.`);
  }

  for (let pageNo = 2; pageNo <= Math.min(pageCount, PZU_MAX_PAGES); pageNo += 1) {
    let result = null;
    for (let attempt = 0; attempt < 2 && !result; attempt += 1) {
      const fetched = await fetchPzuPagingTargets(page, pageNo);
      if (fetched && Array.isArray(fetched.targets) && fetched.targets.length > 0) {
        const assembled = assemblePzuPageRows(fetched.targets, pageRows);
        const paging = parsePzuPagingInfo(assembled.pagingText);
        const expectedFrom = ((pageNo - 1) * pageSize) + 1;
        const expectedTo = Math.min(pageNo * pageSize, total);
        if (paging?.rangeFrom === expectedFrom
            && paging.rangeTo === expectedTo
            && paging.total === total) {
          result = { assembled };
        } else {
          const actualRange = paging
            ? `${paging.rangeFrom}-${paging.rangeTo} z ${paging.total}`
            : "brak zakresu";
          console.error(
            `PZU paginacja strona ${pageNo}: nieoczekiwany zakres (${actualRange})`,
          );
        }
      } else if (fetched && fetched.error) {
        console.error(`PZU paginacja strona ${pageNo}: ${fetched.error}`);
      }
      if (!result && attempt === 0 && retryDelayMs > 0) {
        await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
      }
    }
    if (!result) {
      
      
      
      throw new Error(`Nie udało się pobrać strony ${pageNo} z ${Math.min(pageCount, PZU_MAX_PAGES)} listy polis.`);
    }

    pageRows = result.assembled.rows;
    addRows(pageRows);
    onProgress({ collected: rowsSeen, total, page: pageNo });
    if (pageDelayMs > 0) await new Promise((resolve) => setTimeout(resolve, pageDelayMs));
  }

  if (rowsSeen !== total) {
    throw new Error(`Lista PZU jest niekompletna (przetworzono ${rowsSeen}/${total} wierszy).`);
  }
  return {
    rows: [...rowByPeriodKey.values()],
    total,
    sourceCount: rowsSeen,
    duplicateCount: rowsSeen - rowByPeriodKey.size,
  };
}




function resolvePzuRegistration(product) {
  const p = compactText(product).toLowerCase().replace(/[żź]/g, "z").replace(/ó/g, "o").replace(/ą/g, "a").replace(/ł/g, "l");
  if (!p) return "Inne";
  if (p.includes("wojazer")) return "Podróż";        
  if (p.includes("auto")) return "Auto";             
  if (p.includes("dom")) return "Mieszkanie";        
  if (p.includes("oc ogolna")) return "Firma";       
  return "Inne";
}



function resolvePzuIdentity(regonRaw, peselRaw) {
  const regon = compactText(regonRaw).replace(/\D/g, "");
  const pesel = compactText(peselRaw).replace(/\D/g, "");
  const validRegon = /^\d{9}$/.test(regon) || /^\d{14}$/.test(regon) ? regon : "";
  const validPesel = /^\d{11}$/.test(pesel) ? pesel : "";
  return {
    identityNumber: validRegon || validPesel, 
    regon: validRegon,
    pesel: validPesel,
  };
}




export function mapPzuPolicyRows(rows, {
  minEndDate = "",
  mappingCollector = null,
} = {}) {
  const records = [];
  const seen = new Set();
  const normalizedMinEndDate = normalizePzuDateYmd(minEndDate);
  for (const row of Array.isArray(rows) ? rows : []) {
    const policyNumber = compactText(row?.policyNumber);
    const clientName = compactText(row?.clientName) || compactText(row?.insured);
    const status = compactText(row?.status).slice(0, 80);
    const validFrom = normalizePzuDateYmd(row?.validFrom);
    const validUntil = normalizePzuDateYmd(row?.validUntil);
    if (/anulowan|cancel|storn|odst[aą]pien/i.test(status)) {
      mappingCollector?.recordFiltered("CANCELLED_POLICY");
      continue;
    }
    if (!normalizePzuPolicyKey(policyNumber) || !clientName) {
      let identityHash = "";
      try {
        identityHash = createPortfolioIssueIdentityHash({
          insurer: "PZU",
          sourceId: compactText(row?.id ?? row?.publicId),
          policyNumber,
          validFrom,
          validUntil,
        });
      } catch {
      }
      mappingCollector?.recordRejected("MISSING_REQUIRED_FIELDS", { identityHash });
      continue;
    }
    if (normalizedMinEndDate && validUntil && validUntil < normalizedMinEndDate) {
      mappingCollector?.recordFiltered("OUTSIDE_DATE_RANGE");
      continue;
    }

    const key = makePzuPolicyPeriodKey({ policyNumber, validFrom, validUntil });
    if (seen.has(key)) {
      mappingCollector?.recordDuplicate();
      continue;
    }
    seen.add(key);

    const identity = resolvePzuIdentity(row?.regon, row?.pesel);
    records.push({
      insurer: "PZU",
      policyNumber,
      clientName: clientName.slice(0, 160),
      identityNumber: identity.identityNumber,
      pesel: identity.pesel,   
      regon: identity.regon,
      contact: "",             
      registration: resolvePzuRegistration(row?.product),
      vehicleMake: "",
      vehicleModel: "",
      vin: "",
      validFrom,
      validUntil,
      issueDate: normalizePzuDateYmd(row?.issueDate),
      premium: "",
      status,
      product: compactText(row?.product).slice(0, 80),
      riskSummary: "",
    });
    mappingCollector?.recordMapped();
  }
  return records;
}

function normalizePzuPlate(value) {
  return compactText(value).replace(/[^A-Za-z0-9]/g, "").toUpperCase();
}



function normalizePzuDateYmd(value) {
  const text = compactText(value);
  const iso = text.match(/\b(\d{4})-(\d{2})-(\d{2})\b/);
  if (iso) return `${iso[1]}-${iso[2]}-${iso[3]}`;
  const pl = text.match(/\b(\d{2})[./-](\d{2})[./-](\d{4})\b/);
  if (pl) return `${pl[3]}-${pl[2]}-${pl[1]}`;
  return "";
}




export function filterPzuRowsSince(rows, sinceDate) {
  const list = Array.isArray(rows) ? rows : [];
  if (!sinceDate) return list;
  return list.filter((row) => {
    const issueDate = normalizePzuDateYmd(row?.issueDate);
    if (issueDate) return issueDate >= sinceDate;
    const startDate = normalizePzuDateYmd(row?.validFrom);
    if (startDate) return startDate >= sinceDate;
    return true;
  });
}



function applyPzuVehicleInfo(record, vehicleInfo) {
  if (!record || !vehicleInfo) return false;
  let changed = false;
  const plate = normalizePzuPlate(vehicleInfo.registrationNumber);
  if (plate.length >= 4 && plate.length <= 10 && record.registration === "Auto") {
    record.registration = plate;
    changed = true;
  }
  if (!record.vehicleMake && compactText(vehicleInfo.brand)) {
    record.vehicleMake = compactText(vehicleInfo.brand).slice(0, 60);
    changed = true;
  }
  if (!record.vehicleModel && compactText(vehicleInfo.model)) {
    record.vehicleModel = compactText(vehicleInfo.model).slice(0, 60);
    changed = true;
  }
  const vin = normalizePzuPlate(vehicleInfo.vin).slice(0, 24);
  if (!record.vin && vin.length >= 11) {
    record.vin = vin;
    changed = true;
  }
  return changed;
}





export async function enrichPzuVehicleInfo(page, records, { onProgress = () => {}, chunkSize = 15 } = {}) {
  const targets = (Array.isArray(records) ? records : [])
    .filter((record) => record && record.registration === "Auto" && record.policyNumber);
  let done = 0;
  let filled = 0;
  for (let start = 0; start < targets.length; start += chunkSize) {
    const chunk = targets.slice(start, start + chunkSize);
    const results = await page.evaluate(async (numbers) => {
      const out = {};
      for (const number of numbers) {
        
        
        
        
        
        const controller = new AbortController();
        const timer = setTimeout(() => controller.abort(), 10000);
        try {
          const response = await fetch("/pc/everest/api/v1/policies/search", {
            method: "POST",
            credentials: "include",
            headers: { "Content-Type": "application/json", Accept: "application/json" },
            body: JSON.stringify({ policyNumber: number }),
            signal: controller.signal,
          });
          if (!response.ok) { out[number] = null; continue; }
          const data = await response.json().catch(() => null);
          const info = Array.isArray(data) && data[0] ? data[0].policyVehicleInfo : null;
          out[number] = info
            ? {
                registrationNumber: String(info.registrationNumber || ""),
                brand: String(info.brand || ""),
                model: String(info.model || ""),
                vin: String(info.vin || ""),
              }
            : null;
        } catch {
          out[number] = null;
        } finally {
          clearTimeout(timer);
        }
        await new Promise((resolve) => setTimeout(resolve, 120));
      }
      return out;
    }, chunk.map((record) => record.policyNumber));

    for (const record of chunk) {
      if (applyPzuVehicleInfo(record, results[record.policyNumber])) filled += 1;
      done += 1;
    }
    onProgress({ done, total: targets.length, filled });
  }
  return { enriched: filled, checked: targets.length };
}


export function normalizePzuPremium(value) {
  const text = compactText(value).replace(/\s*PLN\s*$/i, "").trim();
  return /^\d[\d\s ]*,\d{2}$/.test(text) ? text.replace(/ /g, " ") : "";
}







export async function enrichPzuPremiums(page, records, { onProgress = () => {} } = {}) {
  const targets = (Array.isArray(records) ? records : [])
    .filter((record) => record && record.policyNumber && !record.premium);
  let done = 0;
  let filled = 0;
  for (const record of targets) {
    let premium = "";
    for (let attempt = 0; attempt < 2 && !premium; attempt += 1) {
      const raw = await page.evaluate(async (policyNumber) => {
        const form = document.querySelector("#gw-root-form");
        if (!form) return { error: "brak formularza" };
        const fd = new FormData(form);
        const params = new URLSearchParams();
        for (const [key, value] of fd.entries()) {
          if (typeof value === "string") params.append(key, value);
        }
        params.set("TabBar-PolicyTab-PolicyTab_PolicyRetrievalItem", policyNumber);
        params.set("eventSource", "TabBar-PolicyTab-PolicyTab_PolicyRetrievalItem_Button_act");
        const controller = new AbortController();
        const timer = setTimeout(() => controller.abort(), 30000);
        try {
          const response = await fetch(form.action, {
            method: "POST",
            credentials: "include",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: params.toString(),
            signal: controller.signal,
          });
          if (!response.ok) return { error: "HTTP " + response.status };
          const data = await response.json().catch(() => null);
          if (!data || !Array.isArray(data.targets)) return { error: "odpowiedź bez targets" };
          for (const target of data.targets) {
            if (typeof target.content !== "string" || target.content.indexOf("PolicyPerCost") === -1) continue;
            const doc = new DOMParser().parseFromString(target.content, "text/html");
            const el = doc.querySelector('[id$="PolicyPerCost"]');
            if (el) return { premium: String(el.textContent || "").trim() };
          }
          return { premium: "" };
        } catch (error) {
          return { error: String(error && error.message || error) };
        } finally {
          clearTimeout(timer);
        }
      }, record.policyNumber).catch(() => null);
      if (raw && raw.error) console.error(`PZU składka ${record.policyNumber}: ${raw.error}`);
      premium = normalizePzuPremium(raw && raw.premium);
      if (!premium && attempt === 0) await new Promise((resolve) => setTimeout(resolve, 1200));
    }
    if (premium) {
      record.premium = premium;
      filled += 1;
    }
    done += 1;
    onProgress({ done, total: targets.length, filled });
    await new Promise((resolve) => setTimeout(resolve, 150));
  }
  return { enriched: filled, checked: targets.length };
}


export const __testables = { resolvePzuRegistration, resolvePzuIdentity, normalizePzuPlate, applyPzuVehicleInfo, normalizePzuDateYmd, assemblePzuPageRows, parsePzuPagingInfo };
