















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

const GENERALI_FRONTEND_BASE = "https://portal.generali.pl/frontend/";
const GENERALI_DOWNLOAD_POLICY_URL = "https://portal.generali.pl/frontend/printoutSender/downloadPolicy";
const GENERALI_API_PAGE_SIZE = 100;
const GENERALI_API_MAX_PAGES = 60;

const GENERALI_CREATE_DATE_FROM = "2016-01-01";
export const GENERALI_PORTFOLIO_COMPLETENESS_ERROR = "PORTFOLIO_COMPLETENESS_FAILED";

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

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

function getGeneraliSourceIdentity(item) {
  const sourceId = compactText(item?.policyId);
  if (sourceId) return `id:${sourceId}`;
  const policyNumber = compactText(item?.policyNo || item?.offerOrPolicyNo)
    .replace(/[^0-9A-Za-z]/g, "")
    .toUpperCase();
  if (!policyNumber) return "";
  return [
    "period",
    policyNumber,
    compactText(item?.startDate).slice(0, 10),
    compactText(item?.endDate).slice(0, 10),
  ].join("\0");
}

function addGeneraliPageSourceIdentities(items, identitiesFromEarlierPages) {
  const identitiesFromCurrentPage = new Set();
  for (const item of Array.isArray(items) ? items : []) {
    const identity = getGeneraliSourceIdentity(item);
    if (!identity) continue;
    if (identitiesFromEarlierPages.has(identity)) {
      throwGeneraliCompletenessError("repeated_item_across_pages");
    }
    identitiesFromCurrentPage.add(identity);
  }
  for (const identity of identitiesFromCurrentPage) {
    identitiesFromEarlierPages.add(identity);
  }
}

function throwGeneraliCompletenessError(reason) {
  const error = new Error(`Import Generali przerwany: nie udało się potwierdzić kompletności listy polis (${reason}).`);
  error.code = GENERALI_PORTFOLIO_COMPLETENESS_ERROR;
  error.reason = reason;
  throw error;
}



export function resolveGeneraliSearchUrl(capturedUrl) {
  const candidate = compactText(capturedUrl);
  if (!candidate || candidate.length > 32_000 || candidate.includes("\0")) return "";
  let resolved;
  try {
    resolved = new URL(candidate, GENERALI_FRONTEND_BASE);
  } catch {
    return "";
  }
  if (
    resolved.protocol !== "https:"
    || resolved.hostname !== "portal.generali.pl"
    || resolved.port !== ""
    || resolved.username !== ""
    || resolved.password !== ""
    || resolved.pathname !== "/frontend/search/policy"
  ) {
    return "";
  }
  return resolved.href;
}



export function buildGeneraliSearchUrl(templateUrl, { createDateFrom, pageNumber, pageSize } = {}) {
  let url = resolveGeneraliSearchUrl(templateUrl);
  if (!url) {
    throw new Error("Nieprawidłowy adres wyszukiwarki polis Generali.");
  }
  const setParam = (name, value) => {
    const pattern = new RegExp(`([?&]${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=)[^&]*`);
    if (pattern.test(url)) {
      url = url.replace(pattern, `$1${encodeURIComponent(value)}`);
    } else {
      url += `${url.includes("?") ? "&" : "?"}${name}=${encodeURIComponent(value)}`;
    }
  };
  setParam("createDateFrom", createDateFrom || GENERALI_CREATE_DATE_FROM);
  setParam("createDateTo", "");
  setParam("pageSize", String(pageSize || GENERALI_API_PAGE_SIZE));
  setParam("policyPageNumber", String(pageNumber || 1));
  setParam("searchType", "POLICY");
  setParam("searchTab", "POLICY");
  return url;
}



export async function captureGeneraliSearchTemplate(page, { timeoutMs = 45000 } = {}) {
  await page.evaluate(() => {
    if (window.__bpSearchHooked) {
      window.__bpSearchUrl = "";
      return;
    }
    window.__bpSearchHooked = true;
    window.__bpSearchUrl = "";
    const origOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (method, url, ...rest) {
      try {
        if (!window.__bpSearchUrl && /search\/policy\?/.test(String(url))) {
          window.__bpSearchUrl = String(url);
        }
      } catch {
      }
      return origOpen.call(this, method, url, ...rest);
    };
  });
  
  await page.evaluate(() => {
    const tab = Array.from(document.querySelectorAll("li.fe-search-tab, [role='tab'], li"))
      .find((element) => (element.innerText || "").trim().replace(/\s+/g, " ").startsWith("Polisy"));
    if (tab) tab.click();
  }).catch(() => {});
  await delay(1200);
  const searchClicked = await page.evaluate(() => {
    const button = Array.from(document.querySelectorAll("button, input[type='submit']"))
      .find((element) => (element.innerText || element.value || "").trim() === "Szukaj");
    if (!button) return false;
    button.click();
    return true;
  }).catch(() => false);
  if (!searchClicked) {
    throw new Error("Nie udało się otworzyć wyszukiwarki polis Generali.");
  }
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const captured = await page.evaluate(() => window.__bpSearchUrl || "").catch(() => "");
    const resolved = resolveGeneraliSearchUrl(captured);
    if (resolved) return resolved;
    await delay(500);
  }
  throw new Error("Wyszukiwarka polis Generali nie odpowiedziała w wyznaczonym czasie.");
}


async function fetchGeneraliSearchPage(page, { templateUrl, createDateFrom, pageNumber }) {
  const url = buildGeneraliSearchUrl(templateUrl, { createDateFrom, pageNumber, pageSize: GENERALI_API_PAGE_SIZE });
  return page.evaluate(async (target) => {
    try {
      const response = await fetch(target, { credentials: "include", headers: { Accept: "application/json" } });
      const data = await response.json().catch(() => null);
      const value = data && data.responseObject ? data.responseObject : null;
      const policies = value && Array.isArray(value.policies) ? value.policies : [];
      const rawTotal = value?.countPolicies;
      const hasNumericTotal = (typeof rawTotal === "number" && Number.isSafeInteger(rawTotal))
        || (typeof rawTotal === "string" && /^\d+$/.test(rawTotal.trim()));
      const total = hasNumericTotal ? Number(rawTotal) : Number.NaN;
      return { ok: response.ok && Boolean(value), status: response.status, policies, total };
    } catch {
      return { ok: false, status: 0, policies: [], total: Number.NaN };
    }
  }, url);
}




export async function fetchGeneraliSearchPolicies(page, {
  templateUrl,
  createDateFrom = "",
  onProgress = () => {},
  pageDelayMs = 250,
} = {}) {
  if (!templateUrl) throw new Error("Nie udało się rozpocząć pobierania polis. Spróbuj ponownie.");
  const dateFrom = /^\d{4}-\d{2}-\d{2}$/.test(createDateFrom) ? createDateFrom : GENERALI_CREATE_DATE_FROM;

  const first = await fetchGeneraliSearchPage(page, { templateUrl, createDateFrom: dateFrom, pageNumber: 1 });
  if (!first.ok) {
    console.error(`Import Generali: API wyszukiwarki odpowiedziało błędem (status ${first.status || "sieciowy"}).`);
    throw new Error("Nie udało się pobrać polis. Spróbuj ponownie za chwilę.");
  }
  const total = first.total;
  if (!Number.isSafeInteger(total) || total < 0) throwGeneraliCompletenessError("invalid_count_policies");
  const items = [...first.policies];
  if (items.length !== Math.min(GENERALI_API_PAGE_SIZE, total)) {
    throwGeneraliCompletenessError("short_first_page");
  }
  const sourceIdentities = new Set();
  addGeneraliPageSourceIdentities(first.policies, sourceIdentities);
  const requiredPageQuantity = Math.max(1, Math.ceil(total / GENERALI_API_PAGE_SIZE));
  if (requiredPageQuantity > GENERALI_API_MAX_PAGES) {
    throwGeneraliCompletenessError("page_limit");
  }
  const pageQuantity = requiredPageQuantity;
  onProgress({ collected: items.length, total, page: 1, pageQuantity });

  for (let pageNumber = 2; pageNumber <= pageQuantity && items.length < total; pageNumber += 1) {
    const slice = await fetchGeneraliSearchPage(page, { templateUrl, createDateFrom: dateFrom, pageNumber });
    if (!slice.ok) {
      console.error(`Import Generali: pobieranie przerwane na stronie ${pageNumber} (status ${slice.status || "sieciowy"}).`);
      throw new Error("Pobieranie polis zostało przerwane. Spróbuj ponownie za chwilę.");
    }
    if (!Number.isSafeInteger(slice.total) || slice.total < 0) {
      throwGeneraliCompletenessError("invalid_count_policies");
    }
    if (slice.total !== total) throwGeneraliCompletenessError("count_policies_changed");
    if (slice.policies.length === 0) {
      throw new Error(`Generali zwróciło pustą stronę ${pageNumber}, mimo że pobrano dopiero ${items.length} z ${total} polis.`);
    }
    const expectedCount = Math.min(GENERALI_API_PAGE_SIZE, total - items.length);
    if (slice.policies.length !== expectedCount) throwGeneraliCompletenessError("short_page");
    addGeneraliPageSourceIdentities(slice.policies, sourceIdentities);
    items.push(...slice.policies);
    onProgress({ collected: items.length, total, page: pageNumber, pageQuantity });
    if (pageDelayMs > 0) await delay(pageDelayMs);
  }
  if (items.length !== total) {
    throwGeneraliCompletenessError("count_mismatch");
  }
  return items;
}


const GENERALI_PRODUCT_NAME_LABELS = {
  house: "Mieszkanie",
  rentier: "Mieszkanie",
  group_travel: "Podróż",
  travel_canc: "Podróż",
  nnwi: "Inne",
  nnw: "Inne",
  sme: "Firma",
  agro: "Rolne",
};

const GENERALI_PRODUCT_CODE_LABELS = {
  7013: "Mieszkanie",
  7026: "Mieszkanie",
  7011: "Podróż",
  7012: "Podróż",
  7009: "Inne",
  8013: "Inne",
  7025: "Firma",
};


const GENERALI_MOTOR_PRODUCT_NAMES = new Set(["moto", "mtpl", "ass"]);
const GENERALI_MOTOR_PRODUCT_CODES = new Set([7001, 7101, 7007]);

function isGeneraliMotorProduct(item) {
  const name = compactText(item?.productName).toLowerCase();
  if (GENERALI_MOTOR_PRODUCT_NAMES.has(name)) return true;
  return GENERALI_MOTOR_PRODUCT_CODES.has(Number(item?.productCode));
}

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

function looksLikeGeneraliPlate(value) {
  return /^[A-Z]{1,3}[0-9][A-Z0-9]{2,6}$/.test(value) && value.length >= 4 && value.length <= 8;
}


function parseGeneraliMotorSubject(subject) {
  const [head = "", ...restParts] = String(subject || "").split(",");
  const plate = normalizeGeneraliPlate(head);
  if (!looksLikeGeneraliPlate(plate)) return null;
  const rest = compactText(restParts.join(","));
  const [make = "", ...modelWords] = rest.split(" ");
  return { registration: plate, make: compactText(make), model: compactText(modelWords.join(" ")) };
}

function resolveGeneraliDivisionLabel(item) {
  const byName = GENERALI_PRODUCT_NAME_LABELS[compactText(item?.productName).toLowerCase()];
  if (byName) return byName;
  const byCode = GENERALI_PRODUCT_CODE_LABELS[Number(item?.productCode)];
  if (byCode) return byCode;
  return isGeneraliMotorProduct(item) ? "Auto" : "Inne";
}

function formatGeneraliPremium(amount) {
  const value = Number(amount);
  if (!Number.isFinite(value) || value <= 0) return "";
  const fixed = value.toFixed(2);
  return fixed.endsWith(".00") ? fixed.slice(0, -3) : fixed.replace(".", ",");
}

function isCancelledGeneraliPolicy(item) {
  const status = `${compactText(item?.status)} ${compactText(item?.detailedStatus)} ${compactText(item?.displayStatus)}`;
  return /anulowan|storn|odst[aą]pien/i.test(status);
}





export function mapGeneraliSearchPolicies(items, {
  minEndDate = "",
  mappingCollector = null,
} = {}) {
  const records = [];
  const seen = new Set();
  for (const item of Array.isArray(items) ? items : []) {
    const policyNumber = compactText(item?.policyNo || item?.offerOrPolicyNo);
    const clientName = compactText(item?.customerName);
    const validFrom = compactText(item?.startDate).slice(0, 10);
    const endDate = compactText(item?.endDate).slice(0, 10);
    const normalizedPolicyNumber = policyNumber.replace(/[^0-9A-Za-z]/g, "").toLowerCase();
    if (isCancelledGeneraliPolicy(item)) {
      mappingCollector?.recordFiltered("CANCELLED_POLICY");
      continue;
    }
    if (!normalizedPolicyNumber || !clientName) {
      let identityHash = "";
      try {
        identityHash = createPortfolioIssueIdentityHash({
          insurer: "Generali",
          sourceId: item?.policyId != null ? String(item.policyId) : "",
          policyNumber,
          validFrom,
          validUntil: endDate,
        });
      } catch {
      }
      mappingCollector?.recordRejected("MISSING_REQUIRED_FIELDS", { identityHash });
      continue;
    }
    if (minEndDate && endDate && endDate < minEndDate) {
      mappingCollector?.recordFiltered("OUTSIDE_DATE_RANGE");
      continue;
    }
    const key = [
      normalizedPolicyNumber,
      validFrom,
      endDate,
    ].join("\0");
    if (seen.has(key)) {
      mappingCollector?.recordDuplicate();
      continue;
    }
    seen.add(key);

    const phone = compactText(item?.phoneNumber);
    const email = compactText(item?.email);
    const contactParts = [];
    if (phone) contactParts.push(`Telefon: ${phone}`);
    if (email) contactParts.push(`E-mail: ${email}`);

    const subject = compactText(item?.subject);
    const motor = isGeneraliMotorProduct(item) ? parseGeneraliMotorSubject(subject) : null;
    
    const propertyLabel = GENERALI_PRODUCT_NAME_LABELS[compactText(item?.productName).toLowerCase()]
      || GENERALI_PRODUCT_CODE_LABELS[Number(item?.productCode)];
    const clientAddress = !motor && subject && (propertyLabel === "Mieszkanie" || propertyLabel === "Firma")
      ? subject
      : "";

    records.push({
      insurer: "Generali",
      policyNumber,
      policyId: item?.policyId != null ? String(item.policyId) : "",
      productCode: item?.productCode != null ? String(item.productCode) : "",
      clientName: clientName.slice(0, 160),
      identityNumber: "",
      contact: contactParts.join("\n"),
      clientAddress,
      registration: motor ? motor.registration : resolveGeneraliDivisionLabel(item),
      vehicleMake: motor ? motor.make : "",
      vehicleModel: motor ? motor.model : "",
      vin: "",
      validFrom,
      validUntil: endDate,
      issueDate: compactText(item?.issueDate ?? item?.createDate ?? item?.createdAt).slice(0, 10),
      premium: formatGeneraliPremium(item?.premium),
      status: compactText(item?.displayStatus || item?.status),
      previousPolicyNo: compactText(item?.previousPolicyNo),
      pdfAvailable: false,
    });
    mappingCollector?.recordMapped();
  }
  return records;
}



export function pickGeneraliPolicyHistoryRow(rows) {
  const withHash = (Array.isArray(rows) ? rows : []).filter((row) => row && compactText(row.docHash));
  if (withHash.length === 0) return null;
  const policyRows = withHash.filter((row) => compactText(row.eventType).toUpperCase() === "POLICY");
  if (policyRows.length === 0) return null;
  return policyRows.reduce((best, row) => ((Number(row.orderNo) || 0) > (Number(best.orderNo) || 0) ? row : best));
}




export async function installGeneraliPolicyHistoryHook(page) {
  await page.evaluateOnNewDocument(() => {
    window.__bpPolicyHistory = null;
    const origOpen = XMLHttpRequest.prototype.open;
    const origSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.open = function (method, url, ...rest) {
      this.__bpPolicyHistoryRequest = false;
      try {
        const resolved = new URL(String(url || ""), window.location.href);
        this.__bpPolicyHistoryRequest = String(method || "").toUpperCase() === "POST"
          && resolved.origin === window.location.origin
          && resolved.pathname === "/frontend/policyHistory";
      } catch {
      }
      return origOpen.call(this, method, url, ...rest);
    };
    XMLHttpRequest.prototype.send = function (body) {
      if (this.__bpPolicyHistoryRequest === true) {
        this.addEventListener("load", () => {
          try {
            if (Number(this.status) !== 200) return;
            const responseUrl = new URL(
              String(this.responseURL || ""),
              window.location.href,
            );
            if (
              responseUrl.origin !== window.location.origin
              || responseUrl.pathname !== "/frontend/policyHistory"
            ) {
              return;
            }
            const mediaType = String(
              this.getResponseHeader?.("content-type") || "",
            ).split(";", 1)[0].trim().toLowerCase();
            if (
              mediaType !== "application/json"
              && !/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+\+json$/.test(mediaType)
            ) {
              return;
            }
            const parsed = JSON.parse(this.responseText);
            const parsedPolicyId = String(parsed?.policyId ?? "").trim();
            if (
              !parsed
              || typeof parsed !== "object"
              || Array.isArray(parsed)
              || !parsedPolicyId
              || parsedPolicyId.length > 256
              || parsedPolicyId.includes("\0")
              || !Array.isArray(parsed.policyHistoryRowForms)
            ) {
              return;
            }
            window.__bpPolicyHistory = parsed;
          } catch {
          }
        });
      }
      return origSend.call(this, body);
    };
  });
}



export async function downloadGeneraliPolicyPdfBase64(page, { policyId, productCode }) {
  const normalizedPolicyId = compactText(policyId);
  if (!normalizedPolicyId) return null;
  const detailsUrl = `${GENERALI_FRONTEND_BASE}policy/id/${encodeURIComponent(normalizedPolicyId)}?s=1&code=${encodeURIComponent(compactText(productCode) || "")}`;
  try {
    await page.goto(detailsUrl, { waitUntil: "domcontentloaded", timeout: 60000 });
  } catch {
    return null;
  }
  const deadline = Date.now() + 30000;
  let history = null;
  while (Date.now() < deadline) {
    const captured = await page.evaluate(() => window.__bpPolicyHistory || null).catch(() => null);
    if (captured && String(captured.policyId || "") === normalizedPolicyId) {
      history = captured;
      break;
    }
    await delay(500);
  }
  const row = pickGeneraliPolicyHistoryRow(history?.policyHistoryRowForms);
  if (!row) return null;
  return page.evaluate(async ({ url, pid, docType, docHash }) => {
    try {
      const query = `?policyId=${encodeURIComponent(pid)}&docType=${encodeURIComponent(docType)}&docHash=${encodeURIComponent(docHash)}`;
      const response = await fetch(url + query, { credentials: "include", headers: { Accept: "application/pdf" } });
      if (!response.ok) return null;
      const buffer = await response.arrayBuffer();
      if (!buffer || buffer.byteLength < 1000) return null;
      const bytes = new Uint8Array(buffer);
      if (!(bytes[0] === 0x25 && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46)) return null; 
      let binary = "";
      const chunk = 0x8000;
      for (let i = 0; i < bytes.length; i += chunk) {
        binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunk));
      }
      return { base64: btoa(binary), size: bytes.length };
    } catch {
      return null;
    }
  }, {
    url: GENERALI_DOWNLOAD_POLICY_URL,
    pid: normalizedPolicyId,
    docType: compactText(row.eventType) || "POLICY",
    docHash: compactText(row.docHash),
  });
}
