




import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
import { dirname } from "node:path";
import { PRZYPISANY_KLUCZ_PUBLICZNY_LICENCJI } from "./inccghfibgvxcyvvtkjp.mjs";

const DOCUMENT_VERSION = 1;
const LOGIN_PATTERN = /^[a-z0-9][a-z0-9._-]{2,31}$/;
const MAX_NAZWA = 60;

export function normalizujLoginLicencji(value) {
  const login = String(value || "").trim().toLowerCase();
  return LOGIN_PATTERN.test(login) ? login : "";
}

function normalizujPodpisanaLicencje(raw) {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
  const dokument = typeof raw.dokument === "string" ? raw.dokument : "";
  const podpis = typeof raw.podpis === "string" ? raw.podpis : "";
  return dokument && podpis ? { dokument, podpis } : null;
}

function normalizujDokument(raw) {
  const dokument = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
  const dniDoKonca = Number(dokument.dniDoKonca);
  const zegar = dokument.zegar && typeof dokument.zegar === "object" ? dokument.zegar : {};
  return {
    wersja: DOCUMENT_VERSION,
    login: normalizujLoginLicencji(dokument.login),
    nazwa: String(dokument.nazwa || "").replace(/\s+/g, " ").trim().slice(0, MAX_NAZWA),
    abonamentDo: /^\d{4}-\d{2}-\d{2}$/.test(String(dokument.abonamentDo || "")) ? dokument.abonamentDo : "",
    dniDoKonca: Number.isFinite(dniDoKonca) ? dniDoKonca : null,
    aktualizacjeWlaczone: dokument.aktualizacjeWlaczone !== false,
    sprawdzone: typeof dokument.sprawdzone === "string" ? dokument.sprawdzone : "",
    powod: typeof dokument.powod === "string" ? dokument.powod.slice(0, 40) : "",
    licencja: normalizujPodpisanaLicencje(dokument.licencja),
    
    zegar: {
      najpozniejszaChwila: typeof zegar.najpozniejszaChwila === "string" ? zegar.najpozniejszaChwila : "",
      zapisane: typeof zegar.zapisane === "string" ? zegar.zapisane : "",
    },
  };
}

export function createLicencjaStore({
  storagePath,
  vault,
  trustedPublicKey = PRZYPISANY_KLUCZ_PUBLICZNY_LICENCJI,
} = {}) {
  if (!storagePath) throw new Error("Magazyn licencji wymaga ścieżki pliku.");
  if (typeof trustedPublicKey !== "string" || !trustedPublicKey) {
    throw new Error("Magazyn licencji wymaga przypiętego klucza publicznego.");
  }
  let kolejkaZapisu = Promise.resolve();

  async function czytajDokument() {
    try {
      const raw = await readFile(storagePath, "utf8");
      return normalizujDokument(JSON.parse(raw.replace(/^﻿/, "")));
    } catch {
      return normalizujDokument(null);
    }
  }

  async function zapiszDokument(dokument) {
    await mkdir(dirname(storagePath), { recursive: true }).catch(() => {});
    const temporaryPath = `${storagePath}.tmp`;
    await writeFile(temporaryPath, `${JSON.stringify(dokument, null, 2)}\n`, "utf8");
    await rename(temporaryPath, storagePath);
  }

  function zmienDokument(mutator) {
    const operacja = kolejkaZapisu
      .catch(() => {})
      .then(async () => {
        const dokument = await czytajDokument();
        const wynik = await mutator(dokument);
        await zapiszDokument(dokument);
        return wynik;
      });
    kolejkaZapisu = operacja.then(() => undefined, () => undefined);
    return operacja;
  }

  async function czyHasloZapisane() {
    if (!vault?.hasLicencjaPassword) return false;
    return vault.hasLicencjaPassword().catch(() => false);
  }

  
  
  
  function dniDoKoncaZDaty(abonamentDo) {
    if (!/^\d{4}-\d{2}-\d{2}$/.test(String(abonamentDo || ""))) return null;
    const teraz = new Date();
    const dzisiaj = Date.UTC(teraz.getFullYear(), teraz.getMonth(), teraz.getDate());
    const roznica = Math.round((Date.parse(`${abonamentDo}T00:00:00Z`) - dzisiaj) / 86_400_000);
    return Number.isFinite(roznica) ? roznica : null;
  }

  
  async function pobierzStan() {
    const dokument = await czytajDokument();
    const hasloZapisane = await czyHasloZapisane();
    return {
      skonfigurowana: Boolean(dokument.login && hasloZapisane),
      login: dokument.login,
      nazwa: dokument.nazwa,
      abonamentDo: dokument.abonamentDo,
      
      
      
      dniDoKonca: dokument.abonamentDo
        ? dniDoKoncaZDaty(dokument.abonamentDo)
        : (Number.isFinite(Number(dokument.dniDoKonca)) ? Number(dokument.dniDoKonca) : null),
      aktualizacjeWlaczone: dokument.aktualizacjeWlaczone,
      sprawdzone: dokument.sprawdzone,
      powod: dokument.powod,
    };
  }

  async function zapiszDaneLogowania({ login, haslo } = {}) {
    const normalizowanyLogin = normalizujLoginLicencji(login);
    if (!normalizowanyLogin) throw new Error("Podaj login otrzymany od autora programu.");
    const normalizowaneHaslo = String(haslo ?? "");
    if (!normalizowaneHaslo) throw new Error("Podaj hasło otrzymane od autora programu.");
    if (!vault?.saveLicencjaPassword) throw new Error("Nie można bezpiecznie zapisać hasła na tym komputerze.");
    await vault.saveLicencjaPassword(normalizowaneHaslo);
    return zmienDokument((dokument) => {
      if (dokument.login !== normalizowanyLogin) {
        dokument.nazwa = "";
        dokument.abonamentDo = "";
        dokument.dniDoKonca = null;
        dokument.powod = "";
        dokument.sprawdzone = "";
      }
      dokument.login = normalizowanyLogin;
      dokument.aktualizacjeWlaczone = true;
      return true;
    });
  }

  async function pobierzDaneLogowania() {
    const dokument = await czytajDokument();
    if (!dokument.login || !vault?.loadLicencjaPassword) return null;
    const haslo = await vault.loadLicencjaPassword().catch(() => "");
    if (!haslo) return null;
    return { login: dokument.login, haslo };
  }

  
  
  
  
  async function zapamietajStanZSerwera(dane = {}, { pelnyStan = false } = {}) {
    return zmienDokument((dokument) => {
      if (typeof dane.nazwa === "string" && dane.nazwa.trim()) {
        dokument.nazwa = dane.nazwa.replace(/\s+/g, " ").trim().slice(0, MAX_NAZWA);
      }
      if (typeof dane.abonamentDo === "string" && (pelnyStan || dane.abonamentDo)) {
        dokument.abonamentDo = /^\d{4}-\d{2}-\d{2}$/.test(dane.abonamentDo) ? dane.abonamentDo : "";
      }
      const dniDoKonca = Number(dane.dniDoKonca);
      if (pelnyStan || Number.isFinite(dniDoKonca)) {
        dokument.dniDoKonca = Number.isFinite(dniDoKonca) ? dniDoKonca : null;
      }
      if (dane.aktualizacjeWlaczone !== undefined) {
        dokument.aktualizacjeWlaczone = dane.aktualizacjeWlaczone !== false;
      }
      dokument.powod = typeof dane.powod === "string" ? dane.powod.slice(0, 40) : "";
      dokument.sprawdzone = new Date().toISOString();
      return true;
    });
  }

  async function zapiszLicencje({ dokument, podpis, kluczPubliczny } = {}) {
    const podpisana = normalizujPodpisanaLicencje({ dokument, podpis });
    if (!podpisana) throw new Error("Serwer nie przysłał poprawnej licencji.");
    if (String(kluczPubliczny || "") !== trustedPublicKey) {
      throw new Error("Serwer licencji użył nieznanego klucza podpisu.");
    }
    return zmienDokument((biezacy) => {
      biezacy.licencja = podpisana;
      biezacy.sprawdzone = new Date().toISOString();
      return true;
    });
  }

  async function pobierzLicencje() {
    const dokument = await czytajDokument();
    return dokument.licencja ? { ...dokument.licencja } : null;
  }

  async function pobierzZapamietanyZegar() {
    const dokument = await czytajDokument();
    return dokument.zegar.najpozniejszaChwila;
  }

  
  
  
  async function zapiszZnacznikCzasu(chwila, { zaufaneZrodlo = false } = {}) {
    const nowa = chwila instanceof Date ? chwila.getTime() : Date.parse(String(chwila || ""));
    if (!Number.isFinite(nowa)) return false;
    return zmienDokument((dokument) => {
      const zapisana = Date.parse(dokument.zegar.najpozniejszaChwila || "");
      if (!zaufaneZrodlo && Number.isFinite(zapisana) && zapisana >= nowa) return false;
      dokument.zegar = {
        najpozniejszaChwila: new Date(nowa).toISOString(),
        zapisane: new Date().toISOString(),
      };
      return true;
    });
  }

  async function usunDaneLogowania() {
    if (vault?.deleteLicencjaPassword) await vault.deleteLicencjaPassword().catch(() => {});
    await rm(storagePath, { force: true }).catch(() => {});
    return true;
  }

  return {
    pobierzStan,
    zapiszDaneLogowania,
    pobierzDaneLogowania,
    zapamietajStanZSerwera,
    zapiszLicencje,
    pobierzLicencje,
    pobierzZapamietanyZegar,
    zapiszZnacznikCzasu,
    usunDaneLogowania,
  };
}
