import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { randomUUID } from "node:crypto";








const MAX_VEHICLES = 400;
const MAX_ANSWERS_PER_VEHICLE = 80;
const MAX_LABEL_LENGTH = 160;
const MAX_VALUE_LENGTH = 300;

export function normalizeAnswerLabel(value) {
  return String(value || "")
    .normalize("NFD")
    .replace(/[̀-ͯ]/g, "")
    
    .replace(/ł/g, "l")
    .replace(/Ł/g, "L")
    
    
    .replace(/[^\p{L}\p{N}\s]/gu, " ")
    .replace(/\s+/g, " ")
    .trim()
    .toLowerCase()
    .slice(0, MAX_LABEL_LENGTH);
}




const SENSITIVE_LABEL_PATTERN = new RegExp([
  "(^|\\s)haslo(\\s|$)",
  "captcha",
  "(^|\\s)token(\\s|$)",
  "(^|\\s)sms(\\s|$)",
  "kod (sms|autoryzacyjny|weryfikacyjny|jednorazowy|bezpieczenstwa|dostepu|z wiadomosci|z aplikacji)",
  "kod otrzymany",
  "jednorazow\\w* (kod|haslo)",
  "haslo jednorazow",
  "autoryzacyj",
  "weryfikacyjn",
].join("|"));

export function isSensitiveAnswerLabel(label) {
  const normalized = normalizeAnswerLabel(label);
  if (!normalized) return true;
  return SENSITIVE_LABEL_PATTERN.test(normalized);
}

function normalizeVehicleKey(registration, pesel) {
  const cleanRegistration = String(registration || "").replace(/[^A-Z0-9]/gi, "").toUpperCase().slice(0, 12);
  const cleanPesel = String(pesel || "").replace(/\D/g, "").slice(0, 11);
  if (!cleanRegistration) return "";
  return `${cleanRegistration}|${cleanPesel}`;
}

function sanitizeAnswerEntry(entry) {
  const label = String(entry?.label || "").replace(/\s+/g, " ").trim().slice(0, MAX_LABEL_LENGTH);
  const normalizedLabel = normalizeAnswerLabel(label);
  const value = String(entry?.value ?? "").trim().slice(0, MAX_VALUE_LENGTH);
  const kind = ["text", "number", "date", "datetime", "month", "select", "checkbox", "radio", "combobox"].includes(entry?.kind)
    ? entry.kind
    : "text";
  if (!normalizedLabel) return null;
  if (kind !== "checkbox" && !value) return null;
  return {
    label,
    normalizedLabel,
    kind,
    value,
    checked: entry?.checked === true,
    insurerId: String(entry?.insurerId || "").slice(0, 30),
    updatedAt: Number.isFinite(entry?.updatedAt) ? entry.updatedAt : Date.now(),
  };
}




const QUALIFIER_TOKEN_PATTERN = new RegExp("^(" + [
  "najmlodsz\\w*", "najstarsz\\w*", "wspolwlasciciel\\w*", "wspoluzytkownik\\w*",
  "kierowc\\w*", "poprzedni\\w*", "garazowani\\w*", "zamieszkani\\w*", "zameldowani\\w*",
  "korespondencyj\\w*", "zagranic\\w*", "polsce", "polski", "leasingodawc\\w*",
  "leasingobiorc\\w*", "matki", "ojca", "dziecka", "malzonk\\w*", "wspolubezpieczon\\w*",
  "ubezpiecz\\w*", "szkod\\w*", "bezszkodow\\w*",
].join("|") + ")$");

function labelTokens(normalizedLabel) {
  return String(normalizedLabel || "").split(/[^a-z0-9]+/).filter((token) => token.length > 1);
}





function findMatchingAnswer(answers, questionLabel) {
  const normalized = normalizeAnswerLabel(questionLabel);
  if (!normalized) return null;
  const exact = answers.find((answer) => answer.normalizedLabel === normalized);
  if (exact) return exact;
  if (normalized.length < 6) return null;
  const questionTokens = labelTokens(normalized);
  if (questionTokens.length === 0) return null;
  return answers.find((answer) => {
    if (answer.normalizedLabel.length < 6) return false;
    if (!(answer.normalizedLabel.includes(normalized) || normalized.includes(answer.normalizedLabel))) return false;
    const answerTokens = labelTokens(answer.normalizedLabel);
    if (answerTokens.length === 0) return false;
    const [shorter, longer] = answerTokens.length <= questionTokens.length
      ? [answerTokens, questionTokens]
      : [questionTokens, answerTokens];
    if (!shorter.every((token) => longer.includes(token))) return false;
    return longer
      .filter((token) => !shorter.includes(token))
      .every((token) => !QUALIFIER_TOKEN_PATTERN.test(token));
  }) || null;
}






const DEFAULT_ANSWER_RULES = [
  {
    labelPattern: /celach marketingowych|marketingow/,
    optionPattern: /^nie(\s|$)/,
    uncheckCheckbox: true,
  },
  {
    labelPattern: /rodzaj uzytkowania|sposob uzytkowania|uzytkowanie pojazdu|przeznaczenie pojazdu/,
    optionPattern: /standardow|zwykl|prywatn/,
  },
];

function findDefaultAnswerForField(field) {
  const normalizedLabel = normalizeAnswerLabel(field?.label);
  if (!normalizedLabel) return null;
  for (const rule of DEFAULT_ANSWER_RULES) {
    if (!rule.labelPattern.test(normalizedLabel)) continue;
    if (field?.kind === "checkbox") {
      if (!rule.uncheckCheckbox) return null;
      return { value: "", checked: false, kind: "checkbox" };
    }
    if (!["select", "radio", "combobox"].includes(field?.kind)) return null;
    const options = Array.isArray(field?.options) ? field.options : [];
    const option = options.find((candidate) => rule.optionPattern.test(normalizeAnswerLabel(candidate?.text)));
    if (!option) return null;
    const optionText = String(option.text || "").trim();
    if (!optionText) return null;
    return { value: optionText, checked: false, kind: field.kind };
  }
  return null;
}




export function createAnswerStore({ storagePath, szyfr = null }) {
  let state = { version: 1, updatedAt: "", vehicles: {} };
  let initialized = false;
  
  
  let locked = false;
  let writeChain = Promise.resolve();

  async function init() {
    if (initialized) return;
    initialized = true;
    try {
      const rawBytes = await readFile(storagePath);
      let raw;
      if (szyfr?.czyZaszyfrowany?.(rawBytes)) {
        try {
          raw = szyfr.odszyfruj(rawBytes).toString("utf8");
        } catch {
          locked = true;
          console.error("Magazyn odpowiedzi: plik jest zaszyfrowany, a klucz niedostępny - magazyn wstrzymany.");
          return;
        }
      } else {
        raw = rawBytes.toString("utf8");
      }
      const parsed = JSON.parse(raw);
      if (parsed && typeof parsed === "object" && parsed.version === 1
        && parsed.vehicles && typeof parsed.vehicles === "object") {
        const vehicles = {};
        for (const [key, vehicle] of Object.entries(parsed.vehicles).slice(0, MAX_VEHICLES)) {
          const answers = (Array.isArray(vehicle?.answers) ? vehicle.answers : [])
            .map(sanitizeAnswerEntry)
            .filter(Boolean)
            .slice(0, MAX_ANSWERS_PER_VEHICLE);
          if (answers.length > 0) vehicles[key] = { updatedAt: vehicle?.updatedAt || Date.now(), answers };
        }
        state = { version: 1, updatedAt: String(parsed.updatedAt || ""), vehicles };
      }
    } catch {
      
    }
  }

  function persist() {
    if (locked) return writeChain;
    const snapshot = JSON.stringify({ ...state, updatedAt: new Date().toISOString() }, null, 2);
    writeChain = writeChain
      .catch(() => {})
      .then(async () => {
        await mkdir(dirname(storagePath), { recursive: true }).catch(() => {});
        const temporaryPath = join(dirname(storagePath), `.odpowiedzi-${randomUUID()}.tmp`);
        const contents = szyfr?.aktywny?.() ? szyfr.zaszyfruj(snapshot) : snapshot;
        await writeFile(temporaryPath, contents);
        await rename(temporaryPath, storagePath);
      })
      .catch((error) => {
        console.error("Magazyn odpowiedzi: nie udało się zapisać pliku.", error?.message || error);
      });
    return writeChain;
  }

  function getVehicleAnswers(registration, pesel) {
    const key = normalizeVehicleKey(registration, pesel);
    if (!key) return [];
    return state.vehicles[key]?.answers || [];
  }

  
  function findAnswerForField({ registration, pesel, field }) {
    const label = String(field?.label || "");
    if (isSensitiveAnswerLabel(label)) return null;
    const match = findMatchingAnswer(getVehicleAnswers(registration, pesel), label);
    if (!match) return findDefaultAnswerForField(field);
    return { value: match.value, checked: match.checked, kind: match.kind };
  }

  
  
  
  function createResolver({ registration, pesel }) {
    return (field) => {
      if (field?.kind === "checkbox") return "";
      const answer = findAnswerForField({ registration, pesel, field });
      return answer ? answer.value : "";
    };
  }

  
  
  
  function resolveStoredValue(field, rawAnswer) {
    const value = typeof rawAnswer === "object" ? String(rawAnswer.value ?? "") : String(rawAnswer);
    if (["select", "radio", "combobox"].includes(field?.kind) && Array.isArray(field?.options)) {
      const option = field.options.find((candidate) => String(candidate?.value ?? "") === value);
      const optionText = option ? String(option.text || "").trim() : "";
      if (optionText) return optionText;
    }
    return value;
  }

  
  function recordAnswers({ registration, pesel, insurerId, fields, values }) {
    const key = normalizeVehicleKey(registration, pesel);
    if (!key || !values || typeof values !== "object") return 0;
    const now = Date.now();
    let savedCount = 0;
    const vehicle = state.vehicles[key] || { updatedAt: now, answers: [] };
    const fieldList = Array.isArray(fields) ? fields : [];
    
    
    
    const ambiguousLabels = new Set();
    const seenLabelValues = new Map();
    for (const field of fieldList) {
      const rawAnswer = values[field?.id];
      if (rawAnswer === null || rawAnswer === undefined) continue;
      const normalizedLabel = normalizeAnswerLabel(field?.label);
      if (!normalizedLabel) continue;
      const resolvedValue = resolveStoredValue(field, rawAnswer);
      if (seenLabelValues.has(normalizedLabel) && seenLabelValues.get(normalizedLabel) !== resolvedValue) {
        ambiguousLabels.add(normalizedLabel);
      }
      seenLabelValues.set(normalizedLabel, resolvedValue);
    }
    for (const field of fieldList) {
      const rawAnswer = values[field?.id];
      if (rawAnswer === null || rawAnswer === undefined) continue;
      if (isSensitiveAnswerLabel(field?.label)) continue;
      if (ambiguousLabels.has(normalizeAnswerLabel(field?.label))) continue;
      const value = resolveStoredValue(field, rawAnswer);
      const checked = typeof rawAnswer === "object" ? rawAnswer.checked === true : false;
      const entry = sanitizeAnswerEntry({
        label: field?.label,
        kind: field?.kind,
        value,
        checked,
        insurerId,
        updatedAt: now,
      });
      if (!entry) continue;
      const existingIndex = vehicle.answers.findIndex((answer) => answer.normalizedLabel === entry.normalizedLabel);
      if (existingIndex >= 0) vehicle.answers.splice(existingIndex, 1);
      vehicle.answers.unshift(entry);
      savedCount += 1;
    }
    if (savedCount === 0) return 0;
    vehicle.answers = vehicle.answers.slice(0, MAX_ANSWERS_PER_VEHICLE);
    vehicle.updatedAt = now;
    state.vehicles[key] = vehicle;
    const vehicleKeys = Object.keys(state.vehicles);
    if (vehicleKeys.length > MAX_VEHICLES) {
      
      vehicleKeys
        .sort((left, right) => (state.vehicles[left].updatedAt || 0) - (state.vehicles[right].updatedAt || 0))
        .slice(0, vehicleKeys.length - MAX_VEHICLES)
        .forEach((oldKey) => delete state.vehicles[oldKey]);
    }
    persist();
    return savedCount;
  }

  
  
  async function reinit() {
    await writeChain.catch(() => {});
    initialized = false;
    locked = false;
    state = { version: 1, updatedAt: "", vehicles: {} };
    await init();
  }

  return {
    init,
    reinit,
    findAnswerForField,
    createResolver,
    recordAnswers,
    getVehicleAnswers,
    flush: () => writeChain,
  };
}
