// ==================== GUTSCHRIFT-NUMMERNKREIS ====================
// Verwaltet die (einzeilige) Nummernkreis-Konfiguration und vergibt fortlaufende
// Gutschrift-Nummern transaktional (Row-Lock via SELECT ... FOR UPDATE), damit
// keine Doppelvergaben/Lücken bei gleichzeitigem Anlegen entstehen.

import prisma from '../lib/prisma.js';
import { Prisma } from '@prisma/client';

export interface NumberRangeConfig {
  prefix: string;
  includeYear: boolean;
  separator: string;
  padding: number;
  nextNumber: number;
  resetYearly: boolean;
  currentYear: number | null;
}

// Liefert die Konfiguration; legt bei Bedarf eine Default-Zeile an.
export async function getOrCreateRange() {
  const existing = await prisma.creditNoteNumberRange.findFirst();
  if (existing) return existing;
  return prisma.creditNoteNumberRange.create({ data: {} });
}

// Speichert die vom Admin editierbaren Felder. `nextNumber` ist bewusst
// mit dabei (manuelles Setzen des nächsten Zählers erlaubt), aber validiert.
export async function updateRange(input: Partial<NumberRangeConfig>) {
  const range = await getOrCreateRange();

  const data: Prisma.CreditNoteNumberRangeUpdateInput = {};
  if (typeof input.prefix === 'string') data.prefix = input.prefix.slice(0, 40);
  if (typeof input.includeYear === 'boolean') data.includeYear = input.includeYear;
  if (typeof input.separator === 'string') data.separator = input.separator.slice(0, 5);
  if (typeof input.padding === 'number' && Number.isInteger(input.padding)) {
    data.padding = Math.min(Math.max(input.padding, 1), 10);
  }
  if (typeof input.resetYearly === 'boolean') data.resetYearly = input.resetYearly;
  if (typeof input.nextNumber === 'number' && Number.isInteger(input.nextNumber) && input.nextNumber >= 1) {
    data.nextNumber = input.nextNumber;
  }

  return prisma.creditNoteNumberRange.update({ where: { id: range.id }, data });
}

// Baut die Nummer aus Konfig + Zähler + (optional) Jahr.
function formatNumber(cfg: {
  prefix: string;
  includeYear: boolean;
  separator: string;
  padding: number;
}, value: number, year: number): string {
  const padded = String(value).padStart(cfg.padding, '0');
  const yearPart = cfg.includeYear ? `${year}${cfg.separator}` : '';
  return `${cfg.prefix}${yearPart}${padded}`;
}

// Nur-Vorschau der NÄCHSTEN Nummer, ohne den Zähler zu verbrauchen.
export async function previewNextNumber(): Promise<string> {
  const range = await getOrCreateRange();
  const year = new Date().getFullYear();
  const value = range.resetYearly && range.currentYear !== year ? 1 : range.nextNumber;
  return formatNumber(range, value, year);
}

// Vergibt die nächste Nummer und erhöht den Zähler – transaktional mit
// Row-Lock, damit parallele Anlagen sich nicht dieselbe Nummer greifen.
export async function assignNextNumber(): Promise<string> {
  // Sicherstellen, dass eine Zeile existiert (außerhalb der Lock-Transaktion).
  await getOrCreateRange();

  return prisma.$transaction(async (tx) => {
    const rows = await tx.$queryRaw<Array<{
      id: number;
      prefix: string;
      includeYear: boolean | number;
      separator: string;
      padding: number;
      nextNumber: number;
      resetYearly: boolean | number;
      currentYear: number | null;
    }>>(Prisma.sql`SELECT * FROM CreditNoteNumberRange ORDER BY id ASC LIMIT 1 FOR UPDATE`);

    const row = rows[0];
    const year = new Date().getFullYear();
    const resetYearly = !!row.resetYearly;

    // Jahreswechsel: Zähler zurücksetzen, wenn so konfiguriert.
    const value = resetYearly && row.currentYear !== year ? 1 : row.nextNumber;

    const number = formatNumber(
      {
        prefix: row.prefix,
        includeYear: !!row.includeYear,
        separator: row.separator,
        padding: row.padding,
      },
      value,
      year,
    );

    await tx.creditNoteNumberRange.update({
      where: { id: row.id },
      data: { nextNumber: value + 1, currentYear: year },
    });

    return number;
  });
}
