














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

const COMPENSA_RENEWALS_URL = "https://cportal.compensa.pl/Portal/api/Renewal/FetchRenewals";
const COMPENSA_RENEWALS_HASH = "#/production/renewal";
const COMPENSA_PRINTS_URL = "https://cportal.compensa.pl/Portal/api/Renewal/GetAvaiablePrints";
const COMPENSA_PRINT_FILE_URL = "https://cportal.compensa.pl/Portal/api/Renewal/PrintFile";
const COMPENSA_API_PAGE_SIZE = 100;
const COMPENSA_API_MAX_PAGES = 200;

export const COMPENSA_PORTFOLIO_COMPLETENESS_ERROR = "PORTFOLIO_COMPLETENESS_FAILED";

function throwCompensaCompletenessError(reason) {
  const error = new Error(`Import Compensy przerwany: lista polis wróciła niekompletna (${reason}).`);
  error.code = COMPENSA_PORTFOLIO_COMPLETENESS_ERROR;
  error.reason = reason;
  throw error;
}

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

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

function getCompensaSourceIdentity(item) {
  const sourceId = compactText(item?.Id);
  if (sourceId) return `id:${sourceId}`;
  const policyNumber = compactText(item?.PolicyNumber)
    .replace(/[^0-9A-Za-z]/g, "")
    .toUpperCase();
  if (!policyNumber) return "";
  const validFrom = normalizeCompensaDate(
    item?.StartDate
    ?? item?.CommencementDate
    ?? item?.PolicyStartDate
    ?? item?.InsuranceStartDate
    ?? item?.BeginDate,
  );
  return [
    "period",
    policyNumber,
    validFrom,
    normalizeCompensaDate(item?.ExpirationDate),
  ].join("\0");
}

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

 
export function buildCompensaRenewalsBody(template, { pageNumber, pageSize, expirationDateFrom, expirationDateTo }) {
  const clone = JSON.parse(JSON.stringify(template ?? {}));
  const assign = (node) => {
    if (!node || typeof node !== "object") return;
    if (Array.isArray(node)) {
      node.forEach(assign);
      return;
    }
    for (const key of Object.keys(node)) {
      const normalized = key.toLowerCase();
      if (normalized === "pagenumber") node[key] = pageNumber;
      else if (normalized === "pagesize") node[key] = pageSize;
      else if (normalized === "expirationdatefrom" && expirationDateFrom) node[key] = expirationDateFrom;
      else if (normalized === "expirationdateto" && expirationDateTo) node[key] = expirationDateTo;
      else assign(node[key]);
    }
  };
  assign(clone);
  return clone;
}

 
export async function captureCompensaApiContext(page, { timeoutMs = 60000 } = {}) {
  let bearer = "";
  let template = null;
  const onRequest = (request) => {
    try {
      const requestUrl = request.url();
      if (!requestUrl.includes("/Portal/api/")) return;
      const headers = request.headers();
      const authorization = headers.authorization || headers.Authorization;
      if (!bearer && authorization && /^Bearer\s+\S/i.test(authorization)) bearer = authorization;
      if (!template && requestUrl.includes("Renewal/FetchRenewals") && request.method() === "POST") {
        const postData = request.postData();
        if (postData) {
          try {
            template = JSON.parse(postData);
          } catch {
          }
        }
      }
    } catch {
    }
  };
  const waitFor = async (milliseconds) => {
    const until = Date.now() + milliseconds;
    while (Date.now() < until && (!bearer || !template)) {
      await delay(400);
    }
    return Boolean(bearer && template);
  };

  page.on("request", onRequest);
  try {
    
    
    
    
    await waitForCompensaAppReady(page);
    await page.evaluate((hash) => { globalThis.location.hash = hash; }, COMPENSA_RENEWALS_HASH).catch(() => {});
    if (!(await waitFor(20000))) {
      
      await openCompensaRenewalsFromMenu(page);
      if (!(await waitFor(15000))) {
        await clickCompensaSearchButton(page);
        await waitFor(Math.max(5000, timeoutMs - 40000));
      }
    }
  } finally {
    page.off("request", onRequest);
  }
  if (!bearer || !template) {
    console.error(`Import Compensa: nie przechwycono kontekstu API (token: ${bearer ? "jest" : "brak"}, wzorzec zapytania: ${template ? "jest" : "brak"}).`);
  }
  return { bearer, template };
}

 
async function waitForCompensaAppReady(page, { timeout = 45000 } = {}) {
  const deadline = Date.now() + timeout;
  while (Date.now() < deadline) {
    const ready = await page.evaluate(() => {
      const text = String(document.body?.innerText || "").replace(/\s+/g, " ").trim();
      if (!text || /^loading/i.test(text)) return false;
      return [...document.querySelectorAll("a, button")]
        .some((element) => /wyloguj/i.test(String(element.innerText || "")));
    }).catch(() => false);
    if (ready) return true;
    await delay(500);
  }
  return false;
}

async function openCompensaRenewalsFromMenu(page) {
  return page.evaluate(() => {
    const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
    const menu = [...document.querySelectorAll("a.nav-link")]
      .find((candidate) => /narzędzia/i.test(String(candidate.innerText || "")));
    if (!menu) return false;
    menu.click();
    const entry = [...(menu.parentElement?.querySelectorAll("a") || [])]
      .filter(visible)
      .find((candidate) => /kontynuacje/i.test(String(candidate.innerText || "")));
    if (!entry) return false;
    entry.click();
    return true;
  }).catch(() => false);
}

async function clickCompensaSearchButton(page) {
  return page.evaluate(() => {
    const visible = (element) => element instanceof HTMLElement && element.getClientRects().length > 0;
    const button = [...document.querySelectorAll("button")]
      .filter(visible)
      .find((candidate) => /szukaj/i.test(String(candidate.innerText || "")));
    if (!button) return false;
    button.click();
    return true;
  }).catch(() => false);
}

async function fetchCompensaRenewalsPage(page, { bearer, template, pageNumber, expirationDateFrom, expirationDateTo }) {
  const body = buildCompensaRenewalsBody(template, {
    pageNumber,
    pageSize: COMPENSA_API_PAGE_SIZE,
    expirationDateFrom,
    expirationDateTo,
  });
  return page.evaluate(async ({ url, token, payload }) => {
    
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 45000);
    try {
      const response = await fetch(url, {
        method: "POST",
        credentials: "include",
        headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: token },
        body: JSON.stringify(payload),
        signal: controller.signal,
      });
      if (!response.ok) return { ok: false, status: response.status, items: [], total: 0 };
      const data = await response.json().catch(() => null);
      if (!data || typeof data !== "object") return { ok: false, status: response.status, items: [], total: 0 };
      
      
      const container = data.Response && typeof data.Response === "object" ? data.Response : data;
      const arrayKey = Object.keys(container)
        .find((key) => Array.isArray(container[key]) && container[key].every((entry) => entry && typeof entry === "object"));
      const items = arrayKey ? container[arrayKey] : [];
      const countKey = Object.keys(container)
        .find((key) => /count|total|amount|ilosc|liczba/i.test(key) && Number.isFinite(Number(container[key])));
      return {
        ok: true,
        status: response.status,
        items,
        total: countKey ? Number(container[countKey]) : Number.NaN,
        shape: { keys: Object.keys(container).slice(0, 15), arrayKey: arrayKey || "", countKey: countKey || "" },
      };
    } catch (error) {
      return { ok: false, status: 0, items: [], total: 0, error: String((error && error.message) || error) };
    } finally {
      clearTimeout(timer);
    }
  }, { url: COMPENSA_RENEWALS_URL, token: bearer, payload: body });
}

 
export async function fetchCompensaRenewals(page, {
  bearer,
  template = null,
  onProgress = () => {},
  expirationDateFrom = "2010-01-01T00:00:00",
  expirationDateTo = "2035-12-31T23:59:59",
} = {}) {
  if (!bearer || !template) {
    throw new Error("Nie udało się rozpocząć pobierania polis z Compensy. Spróbuj ponownie.");
  }
  const first = await fetchCompensaRenewalsPage(page, {
    bearer, template, pageNumber: 1, expirationDateFrom, expirationDateTo,
  });
  if (!first.ok) {
    console.error(`Import Compensa: API kontynuacji odpowiedziało błędem (status ${first.status || "sieciowy"}${first.error ? `: ${first.error}` : ""}).`);
    throw new Error("Nie udało się pobrać polis z Compensy. Spróbuj ponownie za chwilę.");
  }
  console.error(`Import Compensa: kształt odpowiedzi - pola [${(first.shape?.keys || []).join(", ")}], lista "${first.shape?.arrayKey || "?"}", licznik "${first.shape?.countKey || "brak"}", pozycji na stronie ${first.items.length}.`);

  if (!first.shape?.arrayKey) {
    throwCompensaCompletenessError("missing_items_array");
  }
  if (!first.shape?.countKey) {
    throwCompensaCompletenessError("missing_authoritative_total");
  }
  const total = Number(first.total);
  if (!Number.isSafeInteger(total) || total < 0) {
    throwCompensaCompletenessError("invalid_authoritative_total");
  }
  const pageQuantity = Math.max(1, Math.ceil(total / COMPENSA_API_PAGE_SIZE));
  if (pageQuantity > COMPENSA_API_MAX_PAGES) throwCompensaCompletenessError("page_limit");
  if (first.items.length !== Math.min(COMPENSA_API_PAGE_SIZE, total)) throwCompensaCompletenessError("short_page");
  const items = [...first.items];
  const sourceIdentities = new Set();
  addCompensaPageSourceIdentities(first.items, sourceIdentities);
  const expectedArrayKey = first.shape.arrayKey;
  const expectedCountKey = first.shape.countKey;
  onProgress({ collected: items.length, total, page: 1, pageQuantity });
  for (let pageNumber = 2; pageNumber <= pageQuantity; pageNumber += 1) {
    const slice = await fetchCompensaRenewalsPage(page, {
      bearer, template, pageNumber, expirationDateFrom, expirationDateTo,
    });
    if (!slice.ok) {
      console.error(`Import Compensa: pobieranie przerwane na stronie ${pageNumber} (status ${slice.status || "sieciowy"}).`);
      throw new Error("Pobieranie polis z Compensy zostało przerwane. Spróbuj ponownie za chwilę.");
    }
    if (
      slice.shape?.arrayKey !== expectedArrayKey
      || slice.shape?.countKey !== expectedCountKey
    ) {
      throwCompensaCompletenessError("response_shape_changed");
    }
    if (Number(slice.total) !== total) throwCompensaCompletenessError("total_changed");
    const expectedCount = Math.min(
      COMPENSA_API_PAGE_SIZE,
      Math.max(0, total - ((pageNumber - 1) * COMPENSA_API_PAGE_SIZE)),
    );
    if (slice.items.length !== expectedCount) throwCompensaCompletenessError("short_page");
    addCompensaPageSourceIdentities(slice.items, sourceIdentities);
    items.push(...slice.items);
    onProgress({ collected: items.length, total, page: pageNumber, pageQuantity });
  }
  if (items.length !== total) throwCompensaCompletenessError("count_mismatch");
  return items;
}

function normalizeCompensaDate(value) {
  const text = compactText(value);
  const match = text.match(/^(\d{4}-\d{2}-\d{2})/);
  return match ? match[1] : "";
}

function classifyCompensaPdfHttpFailure(status) {
  const normalizedStatus = Number(status);
  if (normalizedStatus === 401) return { type: "global_failure", code: "session_expired" };
  if (normalizedStatus === 403) return { type: "global_failure", code: "authorization" };
  if (normalizedStatus === 408) return { type: "global_failure", code: "timeout" };
  if (normalizedStatus === 429) return { type: "global_failure", code: "rate_limited" };
  if (normalizedStatus === 404) return { type: "document_failure", code: "not_found" };
  if (normalizedStatus >= 500) return { type: "global_failure", code: "portal_unavailable" };
  return { type: "document_failure", code: "unknown" };
}

 
export async function downloadCompensaRenewalPdfBase64(page, { bearer, renewalId }) {
  if (!bearer || !renewalId) return { type: "document_failure", code: "missing_identity" };
  return page.evaluate(async ({ token, id, printsUrl, fileUrl }) => {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 45000);
    const paramize = (value, prefix, out) => {
      if (value === null || value === undefined) return;
      if (typeof value !== "object") {
        out.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(String(value))}`);
        return;
      }
      for (const key of Object.keys(value)) paramize(value[key], `${prefix}[${key}]`, out);
    };
    try {
      const headers = { Accept: "application/json", "Content-Type": "application/json", Authorization: token };
      const listResponse = await fetch(printsUrl, {
        method: "POST",
        credentials: "include",
        headers,
        body: JSON.stringify({ Id: id }),
        signal: controller.signal,
      });
      if (!listResponse.ok) return { stage: "list", status: listResponse.status };
      const listData = await listResponse.json().catch(() => null);
      const prints = Array.isArray(listData?.Prints) ? listData.Prints : [];
      if (prints.length === 0) return { stage: "empty", shape: Object.keys(listData || {}).slice(0, 10) };
      
      const described = (entry) => JSON.stringify(entry || "").toLowerCase();
      const printout = prints.find((entry) => /polis/.test(described(entry))) || prints[0];

      const parts = [];
      paramize(printout, "Printout", parts);
      const fileResponse = await fetch(`${fileUrl}?${parts.join("&")}`, {
        method: "GET",
        credentials: "include",
        headers: { Authorization: token },
        signal: controller.signal,
      });
      if (!fileResponse.ok) return { stage: "file", status: fileResponse.status };
      const buffer = await fileResponse.arrayBuffer();
      if (!buffer || buffer.byteLength === 0) return { stage: "file", status: 0, empty: true };
      let binary = "";
      const bytes = new Uint8Array(buffer);
      for (let index = 0; index < bytes.length; index += 1) binary += String.fromCharCode(bytes[index]);
      return {
        stage: "ok",
        base64: btoa(binary),
        isPdf: bytes[0] === 0x25 && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46,
        printKeys: printout && typeof printout === "object" ? Object.keys(printout).slice(0, 10) : [],
        printCount: prints.length,
      };
    } catch (error) {
      return { stage: "error", message: String((error && error.message) || error).slice(0, 120) };
    } finally {
      clearTimeout(timer);
    }
  }, {
    token: bearer,
    id: renewalId,
    printsUrl: COMPENSA_PRINTS_URL,
    fileUrl: COMPENSA_PRINT_FILE_URL,
  }).then((result) => {
    if (result?.stage === "ok" && typeof result.base64 === "string") {
      
      
      if (result.isPdf !== true) return { type: "document_failure", code: "not_a_pdf" };
      return { type: "success", base64: result.base64 };
    }
    if (result?.stage === "empty") return { type: "unsupported", code: "no_prints" };
    if (result?.stage === "error") return { type: "global_failure", code: "network" };
    return classifyCompensaPdfHttpFailure(result?.status);
  }).catch(() => ({ type: "global_failure", code: "unknown" }));
}

 
export function mapCompensaRenewals(items, {
  minEndDate = "",
  mappingCollector = null,
} = {}) {
  const records = [];
  const seen = new Set();
  for (const item of Array.isArray(items) ? items : []) {
    const policyNumber = compactText(item?.PolicyNumber);
    const clientName = compactText(item?.ClientName);
    const sourceStatus = [
      item?.Status,
      item?.RenewalStatus,
      item?.PolicyStatus,
    ].map(compactText).join(" ");
    const validFrom = normalizeCompensaDate(
      item?.StartDate
      ?? item?.CommencementDate
      ?? item?.PolicyStartDate
      ?? item?.InsuranceStartDate
      ?? item?.BeginDate,
    );
    const validUntil = normalizeCompensaDate(item?.ExpirationDate);
    if (/anulowan|cancel|storn|odst[aą]pien/i.test(sourceStatus)) {
      mappingCollector?.recordFiltered("CANCELLED_POLICY");
      continue;
    }
    if (!policyNumber || !clientName) {
      let identityHash = "";
      try {
        identityHash = createPortfolioIssueIdentityHash({
          insurer: "Compensa",
          sourceId: item?.Id != null ? String(item.Id) : "",
          policyNumber,
          validFrom,
          validUntil,
        });
      } catch {
      }
      mappingCollector?.recordRejected("MISSING_REQUIRED_FIELDS", { identityHash });
      continue;
    }
    if (minEndDate && validUntil && validUntil < minEndDate) {
      mappingCollector?.recordFiltered("OUTSIDE_DATE_RANGE");
      continue;
    }
    const key = [
      policyNumber.replace(/[^0-9A-Za-z]/g, "").toLowerCase(),
      validFrom,
      validUntil,
    ].join("\0");
    if (seen.has(key)) {
      mappingCollector?.recordDuplicate();
      continue;
    }
    seen.add(key);
    records.push({
      insurer: "Compensa",
      policyNumber,
      policyId: item?.Id != null ? String(item.Id) : "",
      clientName: clientName.slice(0, 160),
      identityNumber: compactText(item?.ClientCode),
      contact: "",
      clientAddress: "",
      registration: "",
      vehicleMake: "",
      vehicleModel: "",
      vin: "",
      validFrom,
      validUntil,
      
      
      issueDate: normalizeCompensaDate(
        item?.IssueDate
        ?? item?.ConclusionDate
        ?? item?.SignDate
        ?? item?.CreateDate
        ?? item?.CreatedDate,
      ),
      premium: "",
      status: compactText(item?.Product),
      pdfAvailable: false,
    });
    mappingCollector?.recordMapped();
  }
  return records;
}
