import { runPortfolioPdfBatch } from "./ylivitleiyuglkyjgkec.mjs";
import {
  createPortfolioPdfPolicySelectorSet,
  portfolioPdfPolicySelectorMatches,
} from "./abywezhrsiqkqfhujfvq.mjs";

const ALLIANZ_SCAN_LIST_URL = "https://chuck.allianz.pl/web/guest/polisy"
  + "?p_p_id=policylist_WAR_policylist&p_p_lifecycle=2&p_p_state=normal&p_p_mode=view"
  + "&p_p_resource_id=scanList&p_p_cacheability=cacheLevelPage";
const ALLIANZ_SCAN_DOWNLOAD_URL = "https://chuck.allianz.pl/web/guest/polisy"
  + "?p_p_id=policylist_WAR_policylist&p_p_lifecycle=2&p_p_state=normal&p_p_mode=view"
  + "&p_p_resource_id=scanDownload&p_p_cacheability=cacheLevelPage";
const ALLIANZ_PARAM_PREFIX = "_policylist_WAR_policylist_";
const ALLIANZ_SCAN_COLUMNS = [
  "id",
  "type",
  "name",
  "description",
  "created",
  "contentLength",
];
const ALLIANZ_MIN_PDF_BYTES = 1_000;
const ALLIANZ_MAX_PDF_BYTES = 64 * 1024 * 1024;
const ALLIANZ_PDF_CONCURRENCY = 1;

function compactText(value, maximumLength = 512) {
  const text = String(value ?? "").replace(/\s+/g, " ").trim();
  return text.length <= maximumLength && !text.includes("\0") ? text : "";
}

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

function sourcePolicy(entry) {
  const policy = entry?.policy ?? entry;
  return policy && typeof policy === "object" && !Array.isArray(policy)
    ? policy
    : null;
}

function emptyFailedBatch(remaining, code = "unknown") {
  return {
    publicResult: {
      attempted: 0,
      succeeded: 0,
      failed: 0,
      backoff: 0,
      quarantined: 0,
      unsupported: 0,
      deferred: 0,
      remaining: Math.max(0, remaining),
      batchComplete: false,
      circuitOpen: true,
      circuitCode: code,
      manualRetryAvailable: false,
    },
    privateResult: { manualRetryIdentities: [] },
  };
}

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

function buildScanListQuery(source) {
  const params = new URLSearchParams();
  const set = (name, value) => params.set(`${ALLIANZ_PARAM_PREFIX}${name}`, String(value ?? ""));
  set("sEcho", 1);
  set("iColumns", ALLIANZ_SCAN_COLUMNS.length);
  set("sColumns", "");
  set("iDisplayStart", 0);
  set("iDisplayLength", 100);
  ALLIANZ_SCAN_COLUMNS.forEach((column, index) => {
    set(`mDataProp_${index}`, column);
    set(`bSortable_${index}`, "true");
  });
  set("policy_id", source?.id);
  set("system", source?.system);
  set("hashToCompare", source?.hashToCompare);
  return params.toString();
}

function scanCreatedEpoch(scan) {
  const value = Number(scan?.created);
  if (Number.isFinite(value) && value > 0) return value;
  const parsed = Date.parse(compactText(scan?.created));
  return Number.isFinite(parsed) ? parsed : 0;
}

 
export function selectAllianzPolicyScan(scans, {
  policyNumber = "",
  validFrom = "",
} = {}) {
  const policyKey = normalizedPolicyKey(policyNumber);
  const startEpoch = validFrom ? Date.parse(`${validFrom}T00:00:00Z`) : Number.NaN;
  const scored = [];
  for (const scan of Array.isArray(scans) ? scans : []) {
    const name = compactText(scan?.name, 300);
    const description = compactText(scan?.description, 300);
    if (!name || !/\.pdf$/i.test(name)) continue;
    const searchable = `${name} ${description}`;
    if (/(?:infoexpert|email[_ -]?body|wypowied|fenix|zdj[eę]ci|fotograf|aneks)/i.test(searchable)) {
      continue;
    }
    const numberTokens = name.match(/\d{7,12}/g) || [];
    const nameKey = normalizedPolicyKey(name);
    if (policyKey && numberTokens.length > 0 && !nameKey.includes(policyKey)) {
      continue;
    }
    const created = scanCreatedEpoch(scan);
    let score = 0;
    if (policyKey && nameKey.includes(policyKey)) score += 100;
    if (/^W$/i.test(description)) score += 20;
    if (/(?:polisa|policy|ubezpieczeni)/i.test(name)) score += 10;
    if (Number.isFinite(startEpoch) && created > 0) {
      if (created <= startEpoch + (31 * 86400000)) score += 8;
      if (created > startEpoch + (120 * 86400000)) score -= 80;
    }
    scored.push({ scan, score, created });
  }
  scored.sort((left, right) => (
    right.score - left.score
    || (left.created || Number.MAX_SAFE_INTEGER) - (right.created || Number.MAX_SAFE_INTEGER)
  ));
  return scored[0]?.scan || null;
}

function decodePdfResult(result) {
  if (
    typeof result?.base64 !== "string"
    || !Number.isSafeInteger(result?.size)
    || result.size < ALLIANZ_MIN_PDF_BYTES
    || result.size > ALLIANZ_MAX_PDF_BYTES
  ) {
    return { type: "document_failure", code: "invalid_pdf" };
  }
  let bytes;
  try {
    bytes = Buffer.from(result.base64, "base64");
  } catch {
    return { type: "document_failure", code: "invalid_pdf" };
  }
  if (
    bytes.byteLength !== result.size
    || bytes[0] !== 0x25
    || bytes[1] !== 0x50
    || bytes[2] !== 0x44
    || bytes[3] !== 0x46
    || bytes[4] !== 0x2d
  ) {
    return { type: "document_failure", code: "invalid_pdf" };
  }
  return { type: "success", bytes };
}

async function fetchScans(page, source) {
  const result = await page.evaluate(async ({ url, query }) => {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 30_000);
    try {
      const response = await fetch(`${url}&${query}`, {
        credentials: "include",
        headers: {
          Accept: "application/json, text/javascript, */*; q=0.01",
          "X-Requested-With": "XMLHttpRequest",
        },
        signal: controller.signal,
      });
      if (!response.ok) return { kind: "http", status: response.status };
      const payload = await response.json().catch(() => null);
      const scans = payload?.resultSet?.aaData;
      return Array.isArray(scans)
        ? { kind: "success", scans }
        : { kind: "invalid_response" };
    } catch (error) {
      return { kind: error?.name === "AbortError" ? "timeout" : "network" };
    } finally {
      clearTimeout(timer);
    }
  }, {
    url: ALLIANZ_SCAN_LIST_URL,
    query: buildScanListQuery(source),
  });
  return result;
}

async function fetchPdf(page, scan) {
  const params = new URLSearchParams();
  params.set(`${ALLIANZ_PARAM_PREFIX}printId`, String(scan?.printId ?? -1));
  params.set(`${ALLIANZ_PARAM_PREFIX}id`, String(scan?.id ?? ""));
  params.set(`${ALLIANZ_PARAM_PREFIX}uuid`, String(scan?.uuid ?? ""));
  params.set(`${ALLIANZ_PARAM_PREFIX}hashToCompare`, String(scan?.hashToCompare ?? ""));
  return page.evaluate(async ({ url, query, maximumBytes }) => {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 45_000);
    try {
      const response = await fetch(`${url}&${query}`, {
        credentials: "include",
        signal: controller.signal,
      });
      if (!response.ok) return { kind: "http", status: response.status };
      const contentType = String(response.headers.get("content-type") || "").toLowerCase();
      if (contentType.includes("text/html")) return { kind: "session_expired" };
      const buffer = await response.arrayBuffer();
      if (!buffer || buffer.byteLength > maximumBytes) return { kind: "invalid_pdf" };
      const bytes = new Uint8Array(buffer);
      if (
        bytes.byteLength < 5
        || bytes[0] !== 0x25
        || bytes[1] !== 0x50
        || bytes[2] !== 0x44
        || bytes[3] !== 0x46
        || bytes[4] !== 0x2d
      ) {
        return { kind: "invalid_pdf" };
      }
      let binary = "";
      for (let offset = 0; offset < bytes.length; offset += 0x8000) {
        binary += String.fromCharCode(...bytes.subarray(offset, offset + 0x8000));
      }
      return { kind: "success", base64: btoa(binary), size: bytes.byteLength };
    } catch (error) {
      return { kind: error?.name === "AbortError" ? "timeout" : "network" };
    } finally {
      clearTimeout(timer);
    }
  }, {
    url: ALLIANZ_SCAN_DOWNLOAD_URL,
    query: params.toString(),
    maximumBytes: ALLIANZ_MAX_PDF_BYTES,
  });
}

export async function downloadAllianzPolicyPdfResult(page, policy) {
  const sources = Array.isArray(policy?.allianzDocumentSources)
    ? policy.allianzDocumentSources
    : [];
  if (sources.length === 0) {
    return { type: "unsupported", code: "document_not_available" };
  }
  let scanFailure = null;
  for (const source of sources) {
    let scanResult;
    try {
      scanResult = await fetchScans(page, source);
    } catch {
      return { type: "global_failure", code: "navigation" };
    }
    if (scanResult?.kind === "http") return classifyHttpFailure(scanResult.status);
    if (scanResult?.kind === "timeout") return { type: "global_failure", code: "timeout" };
    if (scanResult?.kind === "network") return { type: "global_failure", code: "network" };
    if (scanResult?.kind !== "success") {
      scanFailure = { type: "global_failure", code: "portal_unavailable" };
      continue;
    }
    const scan = selectAllianzPolicyScan(scanResult.scans, policy);
    if (!scan) continue;
    let result;
    try {
      result = await fetchPdf(page, scan);
    } catch {
      return { type: "global_failure", code: "navigation" };
    }
    if (result?.kind === "success") return decodePdfResult(result);
    if (result?.kind === "http") return classifyHttpFailure(result.status);
    if (result?.kind === "session_expired") {
      return { type: "global_failure", code: "session_expired" };
    }
    if (result?.kind === "timeout") return { type: "global_failure", code: "timeout" };
    if (result?.kind === "network") return { type: "global_failure", code: "network" };
    if (result?.kind === "invalid_pdf") {
      return { type: "document_failure", code: "invalid_pdf" };
    }
    return { type: "document_failure", code: "download_failed" };
  }
  return scanFailure || { type: "unsupported", code: "document_not_available" };
}

export function prepareAllianzPortfolioPdfEntries({
  policies,
  skipPolicyNumbers = [],
  knownPolicyNumbers = [],
} = {}) {
  if (!Array.isArray(policies)) throw new TypeError("policies musi być tablicą.");
  const skipSet = createPortfolioPdfPolicySelectorSet(skipPolicyNumbers, {
    insurer: "allianz",
  });
  const knownSet = createPortfolioPdfPolicySelectorSet(knownPolicyNumbers, {
    insurer: "allianz",
  });
  return policies
    .map((policy, index) => ({ policy, index }))
    .filter(({ policy }) => (
      sourcePolicy(policy)
      && compactText(policy.insurer).toLowerCase() === "allianz"
      && normalizedPolicyKey(policy.policyNumber)
      && !portfolioPdfPolicySelectorMatches(skipSet, policy)
    ))
    .map(({ policy, index }) => ({
      policy,
      index,
      known: portfolioPdfPolicySelectorMatches(knownSet, policy),
    }))
    .sort((left, right) => Number(left.known) - Number(right.known) || left.index - right.index)
    .map(({ policy }) => ({ policy }));
}

export async function runAllianzPortfolioPdfBatch({
  page,
  retryStore,
  pdfDirectory,
  policies,
  limit,
  skipPolicyNumbers = [],
  knownPolicyNumbers = [],
  onProgress = () => {},
} = {}) {
  const entries = prepareAllianzPortfolioPdfEntries({
    policies,
    skipPolicyNumbers,
    knownPolicyNumbers,
  });
  if (entries.length === 0) {
    return runPortfolioPdfBatch({
      retryStore,
      insurer: "allianz",
      pdfDirectory,
      entries: [],
      fetchDocument: async () => ({ type: "unsupported", code: "document_not_available" }),
    });
  }
  let processed = 0;
  const planned = Math.min(entries.length, limit ?? entries.length);
  try {
    return await runPortfolioPdfBatch({
      retryStore,
      insurer: "allianz",
      pdfDirectory,
      entries,
      limit,
      concurrency: ALLIANZ_PDF_CONCURRENCY,
      fetchDocument: async ({ entry }) => {
        try {
          return await downloadAllianzPolicyPdfResult(page, entry.policy);
        } finally {
          processed += 1;
          onProgress({ processed, planned });
        }
      },
    });
  } catch {
    return emptyFailedBatch(entries.length, "unknown");
  }
}
