import { Response } from 'express';
import { ApiResponse, AuthRequest } from '../types/index.js';
import { logChange } from '../services/audit.service.js';
import { ApiError } from '../utils/apiError.js';
import * as creditNoteService from '../services/creditNote.service.js';
import { effectiveNumber } from '../services/creditNote.service.js';
import * as numberRangeService from '../services/creditNoteNumberRange.service.js';
import * as deliveryRangeService from '../services/deliveryNoteNumberRange.service.js';
import * as authorizationService from '../services/authorization.service.js';
import { generateCreditNotePdf } from '../services/creditNotePdf.service.js';

// Gutschriften sind ein reiner Mitarbeiter-/Admin-Bereich (interne
// Subventions-Belege). Portal-Kunden dürfen sie weder sehen noch ändern –
// Defense-in-Depth: nicht nur die UI blendet aus, der Endpoint blockt
// Portal-Token explizit.
function blockPortal(req: AuthRequest, res: Response): boolean {
  if (req.user?.isCustomerPortal) {
    res.status(403).json({ success: false, error: 'Kein Zugriff' } as ApiResponse);
    return true;
  }
  return false;
}

function idParam(req: AuthRequest, res: Response, name: string): number | null {
  const raw = req.params[name];
  if (typeof raw !== 'string' || !/^\d+$/.test(raw)) {
    res.status(400).json({ success: false, error: `Ungültige ID: ${raw}` } as ApiResponse);
    return null;
  }
  const parsed = Number.parseInt(raw, 10);
  if (!Number.isInteger(parsed) || parsed < 1) {
    res.status(400).json({ success: false, error: `Ungültige ID: ${raw}` } as ApiResponse);
    return null;
  }
  return parsed;
}

function handleError(res: Response, error: unknown, fallback: string) {
  const status = error instanceof ApiError ? error.statusCode : 500;
  res.status(status).json({
    success: false,
    error: error instanceof Error ? error.message : fallback,
  } as ApiResponse);
}

// ---- Gesamtübersicht (Hauptmenü) ----
// Anders als die übrigen Credit-Note-Endpunkte NICHT staff-only: Portal-Kunden
// dürfen ihre eigenen (+ vertretene) Belege sehen. Scoping über customerIds
// aus dem JWT – eine im Body/Query mitgeschickte customerId hat keinen Effekt.
export async function listAll(req: AuthRequest, res: Response): Promise<void> {
  try {
    // Portal-User: nur eigene + vertretene Kunden MIT Vollmacht.
    // Fail-closed (Pentest R144): Ein Portal-Token wird IMMER gescoped. Fehlt
    // wider Erwarten die customerId (null/0), ergibt das eine LEERE Menge –
    // nicht den Staff-Zweig (der alle Belege zeigen würde).
    let customerIds: number[] | undefined;
    if (req.user?.isCustomerPortal) {
      customerIds = req.user.customerId ? [req.user.customerId] : [];
      if (req.user.customerId) {
        const representedIds: number[] = (req.user as any).representedCustomerIds || [];
        for (const repCustId of representedIds) {
          if (await authorizationService.hasAuthorization(repCustId, req.user.customerId)) {
            customerIds.push(repCustId);
          }
        }
      }
    }

    const page = Math.max(parseInt((req.query.page as string) || '1') || 1, 1);
    const limit = Math.min(Math.max(parseInt((req.query.limit as string) || '50') || 50, 1), 200);
    const search = typeof req.query.search === 'string' ? req.query.search : undefined;

    const result = await creditNoteService.getAllCreditNotes({ customerIds, page, limit, search });

    // Portal-Kunden dürfen keine Überweisungsbelege laden → receiptPath aus der
    // Response entfernen (Beleg-Download bleibt ohnehin staff-only).
    const isPortal = !!req.user?.isCustomerPortal;
    const items = isPortal
      ? result.items.map((cn) => ({ ...cn, receiptPath: null }))
      : result.items;

    res.json({ success: true, data: { items, pagination: result.pagination } } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Laden der Belegübersicht');
  }
}

// ---- Gutschriften pro Vertrag ----

export async function listByContract(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const contractId = idParam(req, res, 'contractId');
    if (contractId === null) return;
    const data = await creditNoteService.getCreditNotesByContract(contractId);
    res.json({ success: true, data } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Laden der Gutschriften');
  }
}

// Vorbelegung fürs Formular: Default-Kundentyp (Firma/Privat aus Kunde) +
// Vorschau der nächsten Gutschrift-Nummer.
export async function formDefaults(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const contractId = idParam(req, res, 'contractId');
    if (contractId === null) return;
    const [defaults, nextNumber] = await Promise.all([
      creditNoteService.getCreditNoteDefaults(contractId),
      numberRangeService.previewNextNumber(),
    ]);
    res.json({ success: true, data: { ...defaults, nextNumber } } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Laden der Vorbelegung');
  }
}

export async function create(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const contractId = idParam(req, res, 'contractId');
    if (contractId === null) return;
    const created = await creditNoteService.createCreditNote(contractId, req.body ?? {}, req.user?.email);
    await logChange({
      req,
      action: 'CREATE',
      resourceType: 'CreditNote',
      resourceId: created.id.toString(),
      label: `Beleg ${effectiveNumber(created) ?? `#${created.id}`} angelegt (${created.type}, ${created.amountGross} ${created.currency}) an Vertrag #${contractId}`,
    });
    res.status(201).json({ success: true, data: created } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Anlegen der Gutschrift');
  }
}

export async function getOne(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const id = idParam(req, res, 'id');
    if (id === null) return;
    const data = await creditNoteService.getCreditNoteById(id);
    if (!data) {
      res.status(404).json({ success: false, error: 'Gutschrift nicht gefunden' } as ApiResponse);
      return;
    }
    res.json({ success: true, data } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Laden der Gutschrift');
  }
}

export async function update(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const id = idParam(req, res, 'id');
    if (id === null) return;
    const updated = await creditNoteService.updateCreditNote(id, req.body ?? {});
    await logChange({
      req,
      action: 'UPDATE',
      resourceType: 'CreditNote',
      resourceId: updated.id.toString(),
      label: `Beleg ${effectiveNumber(updated) ?? `#${updated.id}`} geändert (${updated.type}, ${updated.amountGross} ${updated.currency})`,
    });
    res.json({ success: true, data: updated } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Ändern der Gutschrift');
  }
}

export async function remove(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const id = idParam(req, res, 'id');
    if (id === null) return;
    const deleted = await creditNoteService.deleteCreditNote(id);
    await logChange({
      req,
      action: 'DELETE',
      resourceType: 'CreditNote',
      resourceId: id.toString(),
      label: `Beleg ${effectiveNumber(deleted) ?? `#${deleted.id}`} gelöscht`,
    });
    res.json({ success: true } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Löschen der Gutschrift');
  }
}

// PDF (Gutschrift-Dokument) erzeugen. ZUGFeRD-Embedding folgt (Teil 2).
export async function generatePdf(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const id = idParam(req, res, 'id');
    if (id === null) return;
    const { pdfPath } = await generateCreditNotePdf(id);
    await logChange({
      req, action: 'UPDATE', resourceType: 'CreditNote', resourceId: id.toString(),
      label: 'Gutschrift-PDF erzeugt',
    });
    res.json({ success: true, data: { pdfPath } } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Erzeugen des PDF');
  }
}

// ---- Nummernkreis (Einstellungen) ----

export async function getNumberRange(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const [range, nextNumber] = await Promise.all([
      numberRangeService.getOrCreateRange(),
      numberRangeService.previewNextNumber(),
    ]);
    res.json({ success: true, data: { ...range, preview: nextNumber } } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Laden des Nummernkreises');
  }
}

export async function updateNumberRange(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const range = await numberRangeService.updateRange(req.body ?? {});
    const preview = await numberRangeService.previewNextNumber();
    await logChange({
      req,
      action: 'UPDATE',
      resourceType: 'CreditNoteNumberRange',
      resourceId: range.id.toString(),
      label: `Gutschrift-Nummernkreis geändert (Präfix "${range.prefix}", nächste Nr. ${range.nextNumber})`,
    });
    res.json({ success: true, data: { ...range, preview } } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Speichern des Nummernkreises');
  }
}

// ---- Lieferschein-Nummernkreis (betragslose Sachwerte) ----

export async function getDeliveryNoteNumberRange(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const [range, nextNumber] = await Promise.all([
      deliveryRangeService.getOrCreateRange(),
      deliveryRangeService.previewNextNumber(),
    ]);
    res.json({ success: true, data: { ...range, preview: nextNumber } } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Laden des Lieferschein-Nummernkreises');
  }
}

export async function updateDeliveryNoteNumberRange(req: AuthRequest, res: Response): Promise<void> {
  try {
    if (blockPortal(req, res)) return;
    const range = await deliveryRangeService.updateRange(req.body ?? {});
    const preview = await deliveryRangeService.previewNextNumber();
    await logChange({
      req,
      action: 'UPDATE',
      resourceType: 'DeliveryNoteNumberRange',
      resourceId: range.id.toString(),
      label: `Lieferschein-Nummernkreis geändert (Präfix "${range.prefix}", nächste Nr. ${range.nextNumber})`,
    });
    res.json({ success: true, data: { ...range, preview } } as ApiResponse);
  } catch (error) {
    handleError(res, error, 'Fehler beim Speichern des Lieferschein-Nummernkreises');
  }
}
