




import {
  createHash,
  randomBytes,
  scrypt as scryptCallback,
  timingSafeEqual,
} from "node:crypto";
import { lstat, mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { dirname } from "node:path";
import { promisify } from "node:util";

const scrypt = promisify(scryptCallback);


const ID_ALPHABET = "ABCDEFGHJKMNPQRSTUVWXYZ23456789";
const ID_GROUPS = 3;
const ID_GROUP_LENGTH = 4;
export const ACCOUNT_ID_PATTERN = /^[A-Z2-9]{4}-[A-Z2-9]{4}-[A-Z2-9]{4}$/;
const PIN_PATTERN = /^\d{4,8}$/;
const MAX_NAME_LENGTH = 40;
const MAX_MEMBERS = 200;
const MAX_SERVED_INSURERS = 100;
const INSURER_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
const SCRYPT_KEY_LENGTH = 64;

const PIN_LOCK_BASE_MS = 10 * 1000;
const PIN_LOCK_MAX_MS = 60 * 1000;
const PIN_LOCK_FREE_ATTEMPTS = 3;
const TRANSFER_KEY_BYTES = 32;
const TRANSFER_KEY_PATTERN = /^[A-Za-z0-9_-]{43}$/;
const TRANSFER_KEY_ID_PATTERN = /^[a-f0-9]{32}$/;
const PAIRING_CODE_PREFIX = "SEK2";

function normalizeTransferKey(value) {
  const transferKey = String(value || "");
  if (!TRANSFER_KEY_PATTERN.test(transferKey)) return "";
  try {
    const bytes = Buffer.from(transferKey, "base64url");
    return bytes.length === TRANSFER_KEY_BYTES && bytes.toString("base64url") === transferKey
      ? transferKey
      : "";
  } catch {
    return "";
  }
}

function normalizeTransferKeyId(value) {
  const keyId = String(value || "");
  return TRANSFER_KEY_ID_PATTERN.test(keyId) ? keyId : "";
}

function transferKeyId(transferKey) {
  const normalizedKey = normalizeTransferKey(transferKey);
  if (!normalizedKey) return "";
  return createHash("sha256")
    .update(Buffer.from(normalizedKey, "base64url"))
    .digest("hex")
    .slice(0, 32);
}

function generateTransferCredentials() {
  const transferKey = randomBytes(TRANSFER_KEY_BYTES).toString("base64url");
  return {
    transferKey,
    keyId: transferKeyId(transferKey),
  };
}

export function formatAgencyPairingCode({
  centralAccountId,
  targetAccountId,
  transferKey,
} = {}) {
  const centralId = normalizeAccountId(centralAccountId);
  const targetId = normalizeAccountId(targetAccountId);
  const normalizedTransferKey = normalizeTransferKey(transferKey);
  if (!centralId || !targetId || !normalizedTransferKey) {
    const error = new Error("Nie można przygotować bezpiecznego kodu parowania.");
    error.code = "PAIRING_INVALID";
    throw error;
  }
  return [
    PAIRING_CODE_PREFIX,
    centralId.replaceAll("-", ""),
    targetId.replaceAll("-", ""),
    normalizedTransferKey,
  ].join(".");
}

export function parseAgencyPairingCode(value) {
  const pairingCode = String(value || "");
  const parts = pairingCode.split(".");
  if (
    parts.length !== 4
    || parts[0] !== PAIRING_CODE_PREFIX
    || !/^[A-Z2-9]{12}$/.test(parts[1])
    || !/^[A-Z2-9]{12}$/.test(parts[2])
  ) {
    const error = new Error("Kod parowania jest nieprawidłowy.");
    error.code = "PAIRING_INVALID";
    throw error;
  }
  const centralAccountId = normalizeAccountId(parts[1]);
  const targetAccountId = normalizeAccountId(parts[2]);
  const transferKey = normalizeTransferKey(parts[3]);
  if (!centralAccountId || !targetAccountId || !transferKey) {
    const error = new Error("Kod parowania jest nieprawidłowy.");
    error.code = "PAIRING_INVALID";
    throw error;
  }
  return {
    centralAccountId,
    targetAccountId,
    transferKey,
    keyId: transferKeyId(transferKey),
  };
}

export function generateAccountId() {
  const groups = [];
  for (let groupIndex = 0; groupIndex < ID_GROUPS; groupIndex += 1) {
    let group = "";
    while (group.length < ID_GROUP_LENGTH) {
      
      const byte = randomBytes(1)[0];
      if (byte < ID_ALPHABET.length * Math.floor(256 / ID_ALPHABET.length)) {
        group += ID_ALPHABET[byte % ID_ALPHABET.length];
      }
    }
    groups.push(group);
  }
  return groups.join("-");
}

export function normalizeAccountId(value) {
  const normalized = String(value || "").trim().toUpperCase().replace(/\s+/g, "");
  const withDashes = /^[A-Z0-9]{12}$/.test(normalized)
    ? `${normalized.slice(0, 4)}-${normalized.slice(4, 8)}-${normalized.slice(8, 12)}`
    : normalized;
  return ACCOUNT_ID_PATTERN.test(withDashes) ? withDashes : "";
}

function normalizeName(value) {
  return String(value || "").replace(/\s+/g, " ").trim().slice(0, MAX_NAME_LENGTH);
}

function normalizePin(value) {
  const pin = String(value || "").trim();
  return PIN_PATTERN.test(pin) ? pin : "";
}

async function hashPin(pin, saltHex) {
  const salt = Buffer.from(saltHex, "hex");
  const derived = await scrypt(pin, salt, SCRYPT_KEY_LENGTH);
  return derived.toString("hex");
}

function normalizeMember(entry) {
  const id = normalizeAccountId(entry?.id);
  const label = normalizeName(entry?.label);
  if (!id || !label) return null;
  const transferKey = normalizeTransferKey(entry?.transferKey);
  const keyId = normalizeTransferKeyId(entry?.keyId);
  return {
    id,
    label,
    addedAt: typeof entry?.addedAt === "string" ? entry.addedAt : new Date().toISOString(),
    ...(transferKey && keyId === transferKeyId(transferKey)
      ? { transferKey, keyId }
      : {}),
  };
}

function normalizeServedInsurers(value, { required = false } = {}) {
  if (!Array.isArray(value)) {
    if (required) throw new Error("Wybierz co najmniej jedno obsługiwane towarzystwo.");
    return null;
  }
  const normalized = [...new Set(value.map((entry) => String(entry || "").trim().toLowerCase())
    .filter((entry) => INSURER_ID_PATTERN.test(entry)))]
    .slice(0, MAX_SERVED_INSURERS);
  if (required && normalized.length === 0) {
    throw new Error("Wybierz co najmniej jedno obsługiwane towarzystwo.");
  }
  return normalized;
}




function normalizeLastCentralExport(value) {
  if (!value || typeof value !== "object") return null;
  const at = typeof value.at === "string" ? value.at : "";
  if (!at) return null;
  const policyCount = Number(value.policyCount);
  return {
    at,
    policyCount: Number.isFinite(policyCount) ? Math.max(0, Math.round(policyCount)) : 0,
    filtered: value.filtered === true,
  };
}

function normalizeEmployeeTransferBinding(account) {
  const centralAccountId = normalizeAccountId(account?.centralAccountId);
  const transferKey = normalizeTransferKey(account?.transferKey);
  const keyId = normalizeTransferKeyId(account?.keyId);
  if (!centralAccountId || !transferKey || !keyId || keyId !== transferKeyId(transferKey)) {
    return null;
  }
  return { centralAccountId, transferKey, keyId };
}

function normalizeDocument(raw) {
  const document = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
  const account = document.account && typeof document.account === "object" ? document.account : null;
  const normalizedAccount = account
    ? {
      id: normalizeAccountId(account.id),
      name: normalizeName(account.name),
      mode: account.mode === "employee" ? "employee" : "main",
      pin: account.pin && typeof account.pin.salt === "string" && typeof account.pin.hash === "string"
        ? { salt: account.pin.salt, hash: account.pin.hash }
        : null,
      createdAt: typeof account.createdAt === "string" ? account.createdAt : new Date().toISOString(),
      pinChangedAt: typeof account.pinChangedAt === "string" ? account.pinChangedAt : "",
      
      
      servedInsurers: normalizeServedInsurers(account.servedInsurers),
      lastCentralExport: normalizeLastCentralExport(account.lastCentralExport),
      
      
      ...(account.mode === "employee"
        ? { allowInsurerImports: account.allowInsurerImports === true }
        : {}),
      ...(account.mode === "employee" && normalizeEmployeeTransferBinding(account)
        ? normalizeEmployeeTransferBinding(account)
        : {}),
    }
    : null;
  const members = Array.isArray(document.members)
    ? document.members.map(normalizeMember).filter(Boolean).slice(0, MAX_MEMBERS)
    : [];
  if (normalizedAccount && (!normalizedAccount.id || !normalizedAccount.name)) {
    
    return { version: 2, account: null, members };
  }
  return { version: 2, account: normalizedAccount, members };
}

function isStrictlyValidStoredDocument(raw, normalized) {
  if (
    !raw
    || typeof raw !== "object"
    || Array.isArray(raw)
    || !raw.account
    || typeof raw.account !== "object"
    || Array.isArray(raw.account)
    || !normalized.account
  ) return false;

  const mode = raw.account.mode === "employee" ? "employee" : "main";
  const pin = raw.account.pin;
  const validPin = Boolean(
    pin
    && typeof pin === "object"
    && !Array.isArray(pin)
    && /^[a-f0-9]{32}$/i.test(pin.salt)
    && /^[a-f0-9]{128}$/i.test(pin.hash),
  );
  if (mode === "main" && !validPin) return false;
  if (mode === "employee" && pin !== null && !validPin) return false;

  if (raw.members !== undefined) {
    if (!Array.isArray(raw.members) || raw.members.length > MAX_MEMBERS) return false;
    if (raw.members.some((member) => !normalizeMember(member))) return false;
    if (raw.members.some((member) => {
      const hasAnyBinding = member.transferKey !== undefined || member.keyId !== undefined;
      if (!hasAnyBinding) return false;
      const transferKey = normalizeTransferKey(member.transferKey);
      const keyId = normalizeTransferKeyId(member.keyId);
      return !transferKey || !keyId || keyId !== transferKeyId(transferKey);
    })) return false;
  }
  if (
    raw.account.servedInsurers !== undefined
    && raw.account.servedInsurers !== null
    && !Array.isArray(raw.account.servedInsurers)
  ) return false;
  if (
    raw.account.allowInsurerImports !== undefined
    && typeof raw.account.allowInsurerImports !== "boolean"
  ) return false;
  if (mode === "employee") {
    const hasAnyBinding = (
      raw.account.centralAccountId !== undefined
      || raw.account.transferKey !== undefined
      || raw.account.keyId !== undefined
    );
    if (hasAnyBinding) {
      const centralAccountId = normalizeAccountId(raw.account.centralAccountId);
      const transferKey = normalizeTransferKey(raw.account.transferKey);
      const keyId = normalizeTransferKeyId(raw.account.keyId);
      if (!centralAccountId || !transferKey || !keyId || keyId !== transferKeyId(transferKey)) {
        return false;
      }
    }
  }
  return true;
}

export function createKontoStore({ storagePath, readExistingAccountId } = {}) {
  let mutationQueue = Promise.resolve();
  let failedPinAttempts = 0;
  let pinLockedUntil = 0;

  async function readDocument({ strictExisting = false } = {}) {
    let strictExistingWasPresent = false;
    if (strictExisting) {
      try {
        const info = await lstat(storagePath);
        if (!info.isFile() || info.isSymbolicLink()) {
          const invalid = new Error("Plik konta nie jest bezpiecznym zwykłym plikiem.");
          invalid.code = "ACCOUNT_STORE_INVALID";
          throw invalid;
        }
        strictExistingWasPresent = true;
      } catch (error) {
        if (error?.code === "ENOENT") {
          return { version: 2, account: null, members: [] };
        }
        if (error?.code === "ACCOUNT_STORE_INVALID") throw error;
        const unreadable = new Error("Nie można bezpiecznie sprawdzić pliku konta.");
        unreadable.code = "ACCOUNT_STORE_UNREADABLE";
        throw unreadable;
      }
    }
    try {
      const raw = await readFile(storagePath, "utf8");
      const parsed = JSON.parse(raw);
      const normalized = normalizeDocument(parsed);
      if (strictExisting && !isStrictlyValidStoredDocument(parsed, normalized)) {
        const error = new Error("Plik konta jest uszkodzony.");
        error.code = "ACCOUNT_STORE_INVALID";
        throw error;
      }
      return normalized;
    } catch (error) {
      if (strictExisting && (error?.code !== "ENOENT" || strictExistingWasPresent)) {
        if (error?.code === "ACCOUNT_STORE_INVALID") throw error;
        const unreadable = new Error("Nie można bezpiecznie odczytać pliku konta.");
        unreadable.code = "ACCOUNT_STORE_UNREADABLE";
        throw unreadable;
      }
      return { version: 2, account: null, members: [] };
    }
  }

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

  
  
  
  function mutateDocument(mutator) {
    const operation = mutationQueue
      .catch(() => {})
      .then(async () => {
        const document = await readDocument();
        const result = await mutator(document);
        await writeDocument(document);
        return result;
      });
    mutationQueue = operation.then(() => undefined, () => undefined);
    return operation;
  }

  function toPublicAccount(account) {
    if (!account) return null;
    return {
      id: account.id,
      name: account.name,
      mode: account.mode,
      pinSet: Boolean(account.pin),
      createdAt: account.createdAt,
      servedInsurers: Array.isArray(account.servedInsurers) ? [...account.servedInsurers] : null,
      lastCentralExport: account.lastCentralExport ? { ...account.lastCentralExport } : null,
      
      allowInsurerImports: account.mode === "employee"
        ? account.allowInsurerImports === true
        : true,
      centralAccountId: account.mode === "employee" ? account.centralAccountId || "" : "",
      keyId: account.mode === "employee" ? account.keyId || "" : "",
      pairingReady: Boolean(
        account.mode === "employee"
        && account.centralAccountId
        && account.transferKey
        && account.keyId,
      ),
    };
  }

  
  async function getStatus({ strictExisting = false } = {}) {
    const document = await readDocument({ strictExisting });
    return {
      exists: Boolean(document.account),
      name: document.account?.name || "",
      mode: document.account?.mode || "main",
      pinSet: Boolean(document.account?.pin),
      lockedForSeconds: getRemainingLockSeconds(),
    };
  }

  
  async function getSnapshot() {
    const document = await readDocument();
    return {
      account: toPublicAccount(document.account),
      members: document.members.map((member) => ({
        id: member.id,
        label: member.label,
        addedAt: member.addedAt,
        keyId: member.keyId || "",
        pairingReady: Boolean(member.transferKey && member.keyId),
      })),
    };
  }

  async function createAccount({ name, pin, mode = "main", id = "", servedInsurers = null } = {}) {
    const normalizedName = normalizeName(name);
    const normalizedPin = normalizePin(pin);
    if (!normalizedName) throw new Error("Podaj nazwę konta.");
    if (!normalizedPin) throw new Error("PIN musi mieć od 4 do 8 cyfr.");
    const normalizedServedInsurers = normalizeServedInsurers(servedInsurers, { required: mode !== "employee" });
    return mutateDocument(async (document) => {
      if (document.account) throw new Error("Konto jest już założone.");
      
      
      let accountId = normalizeAccountId(id);
      if (!accountId && typeof readExistingAccountId === "function") {
        accountId = normalizeAccountId(await readExistingAccountId().catch(() => ""));
      }
      if (!accountId) accountId = generateAccountId();
      const salt = randomBytes(16).toString("hex");
      const hash = await hashPin(normalizedPin, salt);
      const account = {
        id: accountId,
        name: normalizedName,
        mode: mode === "employee" ? "employee" : "main",
        pin: { salt, hash },
        createdAt: new Date().toISOString(),
        pinChangedAt: new Date().toISOString(),
        servedInsurers: normalizedServedInsurers,
      };
      document.account = account;
      return toPublicAccount(account);
    });
  }

  
  async function setInitialPin(pin) {
    const normalizedPin = normalizePin(pin);
    if (!normalizedPin) throw new Error("PIN musi mieć od 4 do 8 cyfr.");
    const salt = randomBytes(16).toString("hex");
    const hash = await hashPin(normalizedPin, salt);
    return mutateDocument((document) => {
      if (!document.account) throw new Error("Brak konta.");
      if (document.account.pin) throw new Error("PIN jest już ustawiony.");
      document.account.pin = { salt, hash };
      document.account.pinChangedAt = new Date().toISOString();
      return toPublicAccount(document.account);
    });
  }

  function getRemainingLockSeconds() {
    const remaining = pinLockedUntil - Date.now();
    return remaining > 0 ? Math.ceil(remaining / 1000) : 0;
  }

  function registerFailedAttempt() {
    failedPinAttempts += 1;
    if (failedPinAttempts >= PIN_LOCK_FREE_ATTEMPTS) {
      const escalation = failedPinAttempts - PIN_LOCK_FREE_ATTEMPTS + 1;
      pinLockedUntil = Date.now() + Math.min(PIN_LOCK_BASE_MS * escalation, PIN_LOCK_MAX_MS);
    }
  }

  async function verifyPin(pin) {
    const lockedForSeconds = getRemainingLockSeconds();
    if (lockedForSeconds > 0) {
      return { ok: false, locked: true, lockedForSeconds };
    }
    const document = await readDocument();
    if (!document.account?.pin) return { ok: false, locked: false, lockedForSeconds: 0 };
    const normalizedPin = normalizePin(pin);
    let matches = false;
    if (normalizedPin) {
      const expected = Buffer.from(document.account.pin.hash, "hex");
      const actual = Buffer.from(await hashPin(normalizedPin, document.account.pin.salt), "hex");
      matches = expected.length === actual.length && timingSafeEqual(expected, actual);
    }
    if (!matches) {
      registerFailedAttempt();
      return { ok: false, locked: false, lockedForSeconds: getRemainingLockSeconds() };
    }
    failedPinAttempts = 0;
    pinLockedUntil = 0;
    return { ok: true, locked: false, lockedForSeconds: 0 };
  }

  async function changePin(currentPin, newPin) {
    const verification = await verifyPin(currentPin);
    if (!verification.ok) {
      const error = new Error(verification.locked || verification.lockedForSeconds
        ? `Zbyt wiele błędnych prób. Spróbuj za ${verification.lockedForSeconds} s.`
        : "Obecny PIN jest nieprawidłowy.");
      error.code = "INVALID_PIN";
      throw error;
    }
    const normalizedPin = normalizePin(newPin);
    if (!normalizedPin) throw new Error("Nowy PIN musi mieć od 4 do 8 cyfr.");
    const salt = randomBytes(16).toString("hex");
    const hash = await hashPin(normalizedPin, salt);
    return mutateDocument((document) => {
      if (!document.account) throw new Error("Brak konta.");
      document.account.pin = { salt, hash };
      document.account.pinChangedAt = new Date().toISOString();
      return true;
    });
  }

  async function renameAccount(name) {
    const normalizedName = normalizeName(name);
    if (!normalizedName) throw new Error("Podaj nazwę konta.");
    return mutateDocument((document) => {
      if (!document.account) throw new Error("Brak konta.");
      if (document.account.mode === "employee") {
        throw new Error("Nazwa tego konta została ustawiona przez centralę i nie można jej zmienić.");
      }
      document.account.name = normalizedName;
      return toPublicAccount(document.account);
    });
  }

  async function setServedInsurers(servedInsurers) {
    const normalizedServedInsurers = normalizeServedInsurers(servedInsurers, { required: true });
    return mutateDocument((document) => {
      if (!document.account) throw new Error("Brak konta.");
      if (document.account.mode === "employee") {
        throw new Error("Obsługiwane towarzystwa ustala centrala agencji.");
      }
      document.account.servedInsurers = normalizedServedInsurers;
      return [...normalizedServedInsurers];
    });
  }

  async function addMember({ id, label } = {}) {
    const normalizedId = normalizeAccountId(id);
    const normalizedLabel = normalizeName(label);
    if (!normalizedId) throw new Error("Nieprawidłowy identyfikator. Poprawny format: XXXX-XXXX-XXXX.");
    if (!normalizedLabel) throw new Error("Podaj nazwę pracownika.");
    return mutateDocument((document) => {
      if (!document.account) throw new Error("Najpierw załóż konto.");
      if (document.account.mode === "employee") throw new Error("Pracowników dodaje się z konta głównego.");
      if (document.account.id === normalizedId) throw new Error("To identyfikator Twojego konta.");
      if (document.members.some((member) => member.id === normalizedId)) {
        throw new Error("Ten identyfikator jest już dodany.");
      }
      if (document.members.length >= MAX_MEMBERS) throw new Error("Osiągnięto limit pracowników.");
      const member = { id: normalizedId, label: normalizedLabel, addedAt: new Date().toISOString() };
      document.members.push(member);
      return { ...member };
    });
  }

  async function updateMemberLabel(id, label) {
    const normalizedId = normalizeAccountId(id);
    const normalizedLabel = normalizeName(label);
    if (!normalizedLabel) throw new Error("Podaj nazwę pracownika.");
    return mutateDocument((document) => {
      const member = document.members.find((entry) => entry.id === normalizedId);
      if (!member) throw new Error("Nie znaleziono pracownika.");
      member.label = normalizedLabel;
      return { ...member };
    });
  }

  async function removeMember(id) {
    const normalizedId = normalizeAccountId(id);
    return mutateDocument((document) => {
      const index = document.members.findIndex((entry) => entry.id === normalizedId);
      if (index < 0) throw new Error("Nie znaleziono pracownika.");
      document.members.splice(index, 1);
      return true;
    });
  }

  
  async function registerGeneratedEmployee(label) {
    const normalizedLabel = normalizeName(label);
    if (!normalizedLabel) throw new Error("Podaj nazwę pracownika.");
    return mutateDocument((document) => {
      if (!document.account) throw new Error("Najpierw załóż konto.");
      if (document.account.mode === "employee") {
        throw new Error("Bazy pracowników przygotowuje się z konta głównego.");
      }
      if (document.members.length >= MAX_MEMBERS) throw new Error("Osiągnięto limit pracowników.");
      let employeeId = generateAccountId();
      while (employeeId === document.account.id || document.members.some((member) => member.id === employeeId)) {
        employeeId = generateAccountId();
      }
      const credentials = generateTransferCredentials();
      const member = {
        id: employeeId,
        label: normalizedLabel,
        addedAt: new Date().toISOString(),
        ...credentials,
      };
      document.members.push(member);
      return {
        ...member,
        centralAccountId: document.account.id,
      };
    });
  }

  
  
  
  async function ensureMemberTransferCredentials(id) {
    const normalizedId = normalizeAccountId(id);
    if (!normalizedId) {
      const error = new Error("Nieprawidłowy identyfikator pracownika.");
      error.code = "PAIRING_INVALID";
      throw error;
    }
    return mutateDocument((document) => {
      if (!document.account || document.account.mode !== "main") {
        const error = new Error("Parowanie pracownika wymaga konta głównego.");
        error.code = "PAIRING_ACCOUNT";
        throw error;
      }
      const member = document.members.find((entry) => entry.id === normalizedId);
      if (!member) {
        const error = new Error("Nie znaleziono pracownika.");
        error.code = "PAIRING_ACCOUNT";
        throw error;
      }
      if (!member.transferKey || !member.keyId) {
        Object.assign(member, generateTransferCredentials());
      }
      return {
        centralAccountId: document.account.id,
        targetAccountId: member.id,
        transferKey: member.transferKey,
        keyId: member.keyId,
      };
    });
  }

  async function getMemberTransferCredentials(id) {
    const normalizedId = normalizeAccountId(id);
    const document = await readDocument();
    if (!document.account || document.account.mode !== "main" || !normalizedId) return null;
    const member = document.members.find((entry) => entry.id === normalizedId);
    if (!member?.transferKey || !member?.keyId) return null;
    return {
      centralAccountId: document.account.id,
      targetAccountId: member.id,
      transferKey: member.transferKey,
      keyId: member.keyId,
    };
  }

  async function getEmployeeTransferCredentials() {
    const document = await readDocument();
    if (
      document.account?.mode !== "employee"
      || !document.account.centralAccountId
      || !document.account.transferKey
      || !document.account.keyId
    ) {
      return null;
    }
    return {
      centralAccountId: document.account.centralAccountId,
      targetAccountId: document.account.id,
      transferKey: document.account.transferKey,
      keyId: document.account.keyId,
    };
  }

  async function pairCentralAccount(pairingCode) {
    const parsed = parseAgencyPairingCode(pairingCode);
    return mutateDocument((document) => {
      if (!document.account || document.account.mode !== "employee") {
        const error = new Error("Parowanie z centralą jest dostępne tylko na koncie pracownika.");
        error.code = "PAIRING_ACCOUNT";
        throw error;
      }
      if (parsed.targetAccountId !== document.account.id) {
        const error = new Error("Kod parowania jest przeznaczony dla innego pracownika.");
        error.code = "PAIRING_TARGET_MISMATCH";
        throw error;
      }
      const existingBinding = (
        document.account.centralAccountId
        || document.account.transferKey
        || document.account.keyId
      );
      if (existingBinding) {
        if (
          document.account.centralAccountId !== parsed.centralAccountId
          || document.account.transferKey !== parsed.transferKey
          || document.account.keyId !== parsed.keyId
        ) {
          const error = new Error("To konto jest już bezpiecznie połączone z inną centralą.");
          error.code = "PAIRING_CONFLICT";
          throw error;
        }
      } else {
        document.account.centralAccountId = parsed.centralAccountId;
        document.account.transferKey = parsed.transferKey;
        document.account.keyId = parsed.keyId;
      }
      return toPublicAccount(document.account);
    });
  }

  
  
  async function markCentralExport({ policyCount = 0, filtered = false } = {}) {
    const normalizedCount = Number(policyCount);
    return mutateDocument((document) => {
      if (!document.account) throw new Error("Brak konta.");
      document.account.lastCentralExport = {
        at: new Date().toISOString(),
        policyCount: Number.isFinite(normalizedCount) ? Math.max(0, Math.round(normalizedCount)) : 0,
        filtered: filtered === true,
      };
      return toPublicAccount(document.account);
    });
  }

  async function isEmployeeMode() {
    const document = await readDocument();
    return document.account?.mode === "employee";
  }

  
  
  async function isPortfolioImportAllowed() {
    const document = await readDocument();
    if (document.account?.mode !== "employee") return true;
    return document.account.allowInsurerImports === true;
  }

  return {
    getStatus,
    getSnapshot,
    createAccount,
    setInitialPin,
    verifyPin,
    changePin,
    renameAccount,
    setServedInsurers,
    addMember,
    updateMemberLabel,
    removeMember,
    registerGeneratedEmployee,
    ensureMemberTransferCredentials,
    getMemberTransferCredentials,
    getEmployeeTransferCredentials,
    pairCentralAccount,
    markCentralExport,
    isEmployeeMode,
    isPortfolioImportAllowed,
  };
}
