import { ContractStatus } from '@prisma/client';
import prisma from '../lib/prisma.js';
import * as appSettingService from './appSetting.service.js';

// Typen für das Cockpit
export type UrgencyLevel = 'critical' | 'warning' | 'ok' | 'none';

export interface CockpitIssue {
  type: string;
  label: string;
  urgency: UrgencyLevel;
  daysRemaining?: number;
  details?: string;
}

export interface CockpitContract {
  id: number;
  contractNumber: string;
  type: string;
  status: ContractStatus;
  customer: {
    id: number;
    customerNumber: string;
    name: string;
  };
  provider?: {
    id: number;
    name: string;
  };
  tariff?: {
    id: number;
    name: string;
  };
  providerName?: string;
  tariffName?: string;
  issues: CockpitIssue[];
  highestUrgency: UrgencyLevel;
}

export interface CockpitSummary {
  totalContracts: number;
  criticalCount: number;
  warningCount: number;
  okCount: number;
  byCategory: {
    cancellationDeadlines: number;
    contractEnding: number;
    missingCredentials: number;
    missingData: number;
    missingInvoices: number;
    openTasks: number;
    pendingContracts: number;
    reviewDue: number;  // Erneute Prüfung fällig (Snooze abgelaufen)
    missingConsents: number;  // Fehlende oder widerrufene Einwilligungen
  };
}

export interface DocumentAlert {
  id: number;
  type: string;         // ID_CARD, PASSPORT, DRIVERS_LICENSE, OTHER
  documentNumber: string;
  expiryDate: string;
  daysUntilExpiry: number;
  urgency: UrgencyLevel;
  customer: {
    id: number;
    customerNumber: string;
    name: string;
  };
}

export interface ReportedMeterReading {
  id: number;
  readingDate: string;
  value: number;
  unit: string;
  notes?: string;
  reportedBy?: string;
  createdAt: string;
  meter: {
    id: number;
    meterNumber: string;
    type: string;
  };
  customer: {
    id: number;
    customerNumber: string;
    name: string;
  };
  // Zugehöriger Vertrag
  contract?: {
    id: number;
    contractNumber: string;
  };
  // Anbieter-Info für Quick-Login
  providerPortal?: {
    providerName: string;
    portalUrl: string;
    portalUsername?: string;
  };
}

export interface CockpitResult {
  contracts: CockpitContract[];
  // Verträge mit hinterlegter Kündigungsbestätigung (Dokument und/oder Datum)
  // in Status ACTIVE/DRAFT/CANCELLED – als eigener Filter im Cockpit.
  cancellationConfirmations: CockpitContract[];
  documentAlerts: DocumentAlert[];
  reportedReadings: ReportedMeterReading[];
  summary: CockpitSummary;
  thresholds: {
    criticalDays: number;
    warningDays: number;
    okDays: number;
  };
}

// Hilfsfunktion: Tage bis zu einem Datum berechnen
function daysUntil(date: Date | null | undefined): number | null {
  if (!date) return null;
  const now = new Date();
  now.setHours(0, 0, 0, 0);
  const target = new Date(date);
  target.setHours(0, 0, 0, 0);
  const diff = target.getTime() - now.getTime();
  return Math.ceil(diff / (1000 * 60 * 60 * 24));
}

// Hilfsfunktion: Urgency basierend auf Tagen bestimmen
function getUrgencyByDays(
  daysRemaining: number | null,
  criticalDays: number,
  warningDays: number,
  okDays: number
): UrgencyLevel {
  if (daysRemaining === null) return 'none';
  if (daysRemaining < 0) return 'critical'; // Bereits überfällig
  if (daysRemaining <= criticalDays) return 'critical';
  if (daysRemaining <= warningDays) return 'warning';
  if (daysRemaining <= okDays) return 'ok';
  return 'none';
}

// Hilfsfunktion: Höchste Dringlichkeit ermitteln
function getHighestUrgency(issues: CockpitIssue[]): UrgencyLevel {
  const levels: UrgencyLevel[] = ['critical', 'warning', 'ok', 'none'];
  for (const level of levels) {
    if (issues.some(i => i.urgency === level)) {
      return level;
    }
  }
  return 'none';
}

// Kündigungsfrist berechnen
function calculateCancellationDeadline(
  endDate: Date | null | undefined,
  cancellationPeriodCode: string | null | undefined
): Date | null {
  if (!endDate || !cancellationPeriodCode) return null;

  const end = new Date(endDate);

  // Parse Kündigungsperiode (z.B. "1M" = 1 Monat, "6W" = 6 Wochen, "14D" = 14 Tage)
  const match = cancellationPeriodCode.match(/^(\d+)([DMWY])$/i);
  if (!match) return null;

  const amount = parseInt(match[1]);
  const unit = match[2].toUpperCase();

  switch (unit) {
    case 'D':
      end.setDate(end.getDate() - amount);
      break;
    case 'W':
      end.setDate(end.getDate() - (amount * 7));
      break;
    case 'M':
      end.setMonth(end.getMonth() - amount);
      break;
    case 'Y':
      end.setFullYear(end.getFullYear() - amount);
      break;
  }

  return end;
}

export async function getCockpitData(opts?: { customerIds?: number[] }): Promise<CockpitResult> {
  // Lade Einstellungen
  const settings = await appSettingService.getAllSettings();
  const criticalDays = parseInt(settings.deadlineCriticalDays) || 14;
  const warningDays = parseInt(settings.deadlineWarningDays) || 42;
  const okDays = parseInt(settings.deadlineOkDays) || 90;
  const docExpiryCriticalDays = parseInt(settings.documentExpiryCriticalDays) || 30;
  const docExpiryWarningDays = parseInt(settings.documentExpiryWarningDays) || 90;

  // Portal-Filter: Wenn customerIds gesetzt sind (Kundenportal-User), beschränken
  // wir ALLE Cockpit-Queries auf diese Customer-IDs. Leeres Array → keine Treffer.
  const customerScopeFilter = opts?.customerIds
    ? { customerId: { in: opts.customerIds } }
    : {};

  // Lade alle relevanten Verträge (inkl. CANCELLED/DEACTIVATED für
  // Schlussrechnung-Check, inkl. ONGOING damit DSGVO-/Consent-Warnungen
  // weiter greifen – Fristen-Blöcke werden pro Vertrag geskippt, s.u.).
  const contracts = await prisma.contract.findMany({
    where: {
      status: {
        in: ['ACTIVE', 'PENDING', 'DRAFT', 'CANCELLED', 'CANCELLED_CONFIRMED', 'DEACTIVATED', 'EXPIRED', 'ONGOING'],
      },
      ...customerScopeFilter,
    },
    include: {
      customer: {
        select: {
          id: true,
          customerNumber: true,
          firstName: true,
          lastName: true,
          companyName: true,
        },
      },
      provider: {
        select: {
          id: true,
          name: true,
        },
      },
      tariff: {
        select: {
          id: true,
          name: true,
        },
      },
      cancellationPeriod: {
        select: {
          code: true,
        },
      },
      address: true,
      bankCard: true,
      identityDocument: true,
      energyDetails: {
        include: {
          meter: true,
          invoices: true,
        },
      },
      internetDetails: {
        include: {
          phoneNumbers: true,
        },
      },
      mobileDetails: {
        include: {
          simCards: true,
        },
      },
      tasks: {
        where: {
          status: 'OPEN',
        },
      },
      // Folgevertrag laden um zu prüfen ob dieser aktiv ist
      followUpContract: {
        select: {
          id: true,
          status: true,
        },
      },
    },
    orderBy: [
      { endDate: 'asc' },
      { createdAt: 'desc' },
    ],
  });

  const cockpitContracts: CockpitContract[] = [];
  const summary: CockpitSummary = {
    totalContracts: 0,
    criticalCount: 0,
    warningCount: 0,
    okCount: 0,
    byCategory: {
      cancellationDeadlines: 0,
      contractEnding: 0,
      missingCredentials: 0,
      missingData: 0,
      missingInvoices: 0,
      openTasks: 0,
      pendingContracts: 0,
      reviewDue: 0,
      missingConsents: 0,
    },
  };

  // Consent-Daten batch-laden für alle (erlaubten) Kunden
  const allConsents = await prisma.customerConsent.findMany({
    where: { status: 'GRANTED', ...customerScopeFilter },
    select: { customerId: true, consentType: true },
  });

  // Map: customerId → Set<consentType>
  const grantedConsentsMap = new Map<number, Set<string>>();
  for (const c of allConsents) {
    if (!grantedConsentsMap.has(c.customerId)) {
      grantedConsentsMap.set(c.customerId, new Set());
    }
    grantedConsentsMap.get(c.customerId)!.add(c.consentType);
  }

  // Widerrufene Consents laden
  const withdrawnConsents = await prisma.customerConsent.findMany({
    where: { status: 'WITHDRAWN', ...customerScopeFilter },
    select: { customerId: true, consentType: true },
  });
  const withdrawnConsentsMap = new Map<number, Set<string>>();
  for (const c of withdrawnConsents) {
    if (!withdrawnConsentsMap.has(c.customerId)) {
      withdrawnConsentsMap.set(c.customerId, new Set());
    }
    withdrawnConsentsMap.get(c.customerId)!.add(c.consentType);
  }

  // Track welche Kunden bereits eine Consent-Warnung bekommen haben (nur einmal pro Kunde)
  const customerConsentWarned = new Set<number>();

  for (const contract of contracts) {
    const issues: CockpitIssue[] = [];

    // SNOOZE-LOGIK: Prüfen ob Snooze aktiv ist (für Fristen-Unterdrückung)
    let snoozeActive = false;
    if (contract.nextReviewDate) {
      const reviewDate = new Date(contract.nextReviewDate);
      const now = new Date();
      now.setHours(0, 0, 0, 0);
      reviewDate.setHours(0, 0, 0, 0);

      if (reviewDate > now) {
        // Snooze aktiv → NUR Fristen-Warnungen unterdrücken, andere Prüfungen laufen weiter
        snoozeActive = true;
      } else {
        // Snooze abgelaufen → "Erneute Prüfung fällig" Warnung
        const daysSince = Math.floor((now.getTime() - reviewDate.getTime()) / (1000 * 60 * 60 * 24));
        issues.push({
          type: 'review_due',
          label: 'Erneute Prüfung fällig',
          urgency: daysSince > 30 ? 'critical' : 'warning',
          daysRemaining: -daysSince,
          details: daysSince === 0
            ? 'Heute zur Prüfung fällig'
            : `Zur Prüfung seit ${daysSince} Tagen fällig`,
        });
        summary.byCategory.reviewDue++;
      }
    }

    // Prüfen ob aktiver Folgevertrag existiert - dann keine Kündigungswarnungen nötig
    const hasActiveFollowUp = contract.followUpContract?.status === 'ACTIVE';

    // Fristen-Blöcke pro Vertrag skippen für Status, bei denen laufende
    // Fristen semantisch keinen Sinn ergeben:
    //   - ONGOING     : "Läuft weiter auf unbestimmte Zeit" → kein Ende
    //   - DEACTIVATED : manuell beendet/archiviert → nichts mehr zu erinnern
    //   - CANCELLED_CONFIRMED : Kündigung vom Anbieter bestätigt →
    //                   Vertragsende steht fest, keine Frist mehr.
    //                   (CANCELLED = nur "Bestätigung abwarten" skippt NICHT,
    //                    damit die "Kündigungsbestätigung fehlt"-Warnung greift.)
    // Die Schlussrechnung-Fehlt-Warnung (Block 13a) bleibt aktiv, weil
    // sie WEGEN dieser Status feuert (kein separater Guard). DSGVO-/
    // Consent-Checks (Block 8) sind pro Kunde, nicht pro Vertrag, und
    // laufen ebenfalls weiter.
    // User-Wunsch 2026-07-13: DEACTIVATED-Vertrag zeigte im Cockpit
    // "Vertrag seit 79 Tagen abgelaufen!" – sinnlos für einen Vertrag,
    // der vom Mitarbeiter aktiv archiviert wurde.
    const skipDeadlines =
      contract.status === 'ONGOING'
      || contract.status === 'DEACTIVATED'
      || contract.status === 'CANCELLED_CONFIRMED';

    // 1. KÜNDIGUNGSFRIST (nur wenn kein aktiver Folgevertrag UND Snooze nicht aktiv)
    // Snooze unterdrückt NUR Fristen-bezogene Warnungen!
    if (!hasActiveFollowUp && !snoozeActive && !skipDeadlines) {
      const cancellationDeadline = calculateCancellationDeadline(
        contract.endDate,
        contract.cancellationPeriod?.code
      );
      const daysToCancellation = daysUntil(cancellationDeadline);
      if (daysToCancellation !== null && daysToCancellation <= okDays) {
        const urgency = getUrgencyByDays(daysToCancellation, criticalDays, warningDays, okDays);
        if (urgency !== 'none') {
          issues.push({
            type: 'cancellation_deadline',
            label: 'Kündigungsfrist',
            urgency,
            daysRemaining: daysToCancellation,
            details: daysToCancellation < 0
              ? `Frist seit ${Math.abs(daysToCancellation)} Tagen überschritten!`
              : `Noch ${daysToCancellation} Tage bis zur Kündigungsfrist`,
          });
          summary.byCategory.cancellationDeadlines++;
        }

        // 1a. KÜNDIGUNG NICHT GESENDET (wenn Frist naht)
        if (!contract.cancellationLetterPath) {
          issues.push({
            type: 'missing_cancellation_letter',
            label: 'Kündigung nicht gesendet',
            urgency,
            details: 'Kündigungsschreiben wurde noch nicht hochgeladen',
          });
          summary.byCategory.missingData++;
        }

        // 1b. KÜNDIGUNGSBESTÄTIGUNG FEHLT (wenn Kündigung gesendet aber keine Bestätigung)
        if (contract.cancellationLetterPath && !contract.cancellationConfirmationPath && !contract.cancellationConfirmationDate) {
          issues.push({
            type: 'missing_cancellation_confirmation',
            label: 'Kündigungsbestätigung fehlt',
            urgency: urgency === 'critical' ? 'critical' : 'warning',
            details: 'Kündigungsbestätigung vom Anbieter wurde noch nicht erhalten',
          });
          summary.byCategory.missingData++;
        }
      }
    }

    // 2. VERTRAGSENDE (nur wenn Snooze nicht aktiv, und nicht ONGOING)
    // Snooze unterdrückt NUR Fristen-bezogene Warnungen!
    if (!snoozeActive && !skipDeadlines) {
      const daysToEnd = daysUntil(contract.endDate);
      if (daysToEnd !== null && daysToEnd <= okDays) {
        const urgency = getUrgencyByDays(daysToEnd, criticalDays, warningDays, okDays);
        if (urgency !== 'none') {
          issues.push({
            type: 'contract_ending',
            label: 'Vertragsende',
            urgency,
            daysRemaining: daysToEnd,
            details: daysToEnd < 0
              ? `Vertrag seit ${Math.abs(daysToEnd)} Tagen abgelaufen!`
              : `Noch ${daysToEnd} Tage bis Vertragsende`,
          });
          summary.byCategory.contractEnding++;
        }
      }
    }

    // 3. FEHLENDE PORTAL-ZUGANGSDATEN
    // Benutzername kann entweder manuell (portalUsername) oder via Stressfrei-Wechseln E-Mail (stressfreiEmailId) gesetzt sein.
    // Vertragsseitiges Opt-out: wenn der Anbieter kein Portal hat oder der
    // Kunde bewusst keine Zugangsdaten pflegt, unterdrückt
    // `portalCredentialsNotRequired` die Warnung. So verstopft die Zeile
    // nicht dauerhaft das Cockpit.
    if (!contract.portalCredentialsNotRequired) {
      const hasUsername = contract.portalUsername || contract.stressfreiEmailId;
      const hasPassword = contract.portalPasswordEncrypted;

      if (!hasUsername || !hasPassword) {
        issues.push({
          type: 'missing_portal_credentials',
          label: 'Portal-Zugangsdaten fehlen',
          urgency: 'warning',
          details: 'Benutzername oder Passwort für das Anbieter-Portal fehlt',
        });
        summary.byCategory.missingCredentials++;
      }
    }

    // 4. KEINE KUNDENNUMMER BEIM ANBIETER
    if (!contract.customerNumberAtProvider) {
      issues.push({
        type: 'missing_customer_number',
        label: 'Kundennummer fehlt',
        urgency: 'warning',
        details: 'Kundennummer beim Anbieter fehlt',
      });
      summary.byCategory.missingData++;
    }

    // 4b. KEINE VERTRAGSNUMMER BEIM ANBIETER
    if (!contract.contractNumberAtProvider) {
      issues.push({
        type: 'missing_contract_number',
        label: 'Vertragsnummer fehlt',
        urgency: 'warning',
        details: 'Vertragsnummer beim Anbieter fehlt',
      });
      summary.byCategory.missingData++;
    }

    // 5. KEIN ANBIETER/TARIF
    if (!contract.providerId && !contract.providerName) {
      issues.push({
        type: 'missing_provider',
        label: 'Anbieter fehlt',
        urgency: 'warning',
        details: 'Kein Anbieter ausgewählt',
      });
      summary.byCategory.missingData++;
    }

    // 6. KEINE ADRESSE
    if (!contract.addressId) {
      issues.push({
        type: 'missing_address',
        label: 'Adresse fehlt',
        urgency: 'warning',
        details: 'Keine Lieferadresse verknüpft',
      });
      summary.byCategory.missingData++;
    }

    // 7. KEINE BANKVERBINDUNG
    // Für DSL, FIBER, CABLE, MOBILE ist dies ein kritisches Problem
    const requiresBankAndId = ['DSL', 'FIBER', 'CABLE', 'MOBILE'].includes(contract.type);
    if (!contract.bankCardId) {
      issues.push({
        type: 'missing_bank',
        label: 'Bankverbindung fehlt',
        urgency: requiresBankAndId ? 'critical' : 'warning',
        details: 'Keine Bankverbindung verknüpft',
      });
      summary.byCategory.missingData++;
    }

    // 7b. KEIN AUSWEIS – nur Mobilfunk. Bei Festnetz/Internet (DSL, FIBER,
    // CABLE) verlangt der Anbieter beim Auftrag keinen Ausweis, die
    // Warnung ist da nur Rauschen. Mobile bleibt drin, weil dort echte
    // Identitätsfeststellung Pflicht ist.
    const requiresIdentityDocument = contract.type === 'MOBILE';
    if (requiresIdentityDocument && !contract.identityDocumentId) {
      issues.push({
        type: 'missing_identity_document',
        label: 'Ausweis fehlt',
        urgency: 'critical',
        details: 'Kein Ausweisdokument verknüpft',
      });
      summary.byCategory.missingData++;
    }

    // 7c. AUSWEIS LÄUFT AB (nur aktive Ausweise prüfen)
    if (contract.identityDocument && contract.identityDocument.isActive && contract.identityDocument.expiryDate) {
      const expiryDate = new Date(contract.identityDocument.expiryDate);
      const today = new Date();
      const daysUntilExpiry = Math.ceil((expiryDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));

      if (daysUntilExpiry < 0) {
        issues.push({
          type: 'identity_document_expired',
          label: 'Ausweis abgelaufen',
          urgency: 'critical',
          details: `Ausweis seit ${Math.abs(daysUntilExpiry)} Tagen abgelaufen (${expiryDate.toLocaleDateString('de-DE')})`,
        });
        summary.byCategory.missingData++;
      } else if (daysUntilExpiry <= docExpiryWarningDays) {
        issues.push({
          type: 'identity_document_expiring',
          label: 'Ausweis läuft ab',
          urgency: daysUntilExpiry <= docExpiryCriticalDays ? 'critical' : 'warning',
          details: `Ausweis läuft in ${daysUntilExpiry} Tagen ab (${expiryDate.toLocaleDateString('de-DE')})`,
        });
        summary.byCategory.cancellationDeadlines++;
      }
    }

    // 8. ENERGIE-SPEZIFISCH: KEIN ZÄHLER
    if (['ELECTRICITY', 'GAS'].includes(contract.type) && contract.energyDetails) {
      if (!contract.energyDetails.meterId) {
        issues.push({
          type: 'missing_meter',
          label: 'Zähler fehlt',
          urgency: 'warning',
          details: 'Kein Zähler verknüpft',
        });
        summary.byCategory.missingData++;
      }
    }

    // 9. MOBIL-SPEZIFISCH: SIM-KARTEN
    if (contract.type === 'MOBILE' && contract.mobileDetails) {
      if (!contract.mobileDetails.simCards || contract.mobileDetails.simCards.length === 0) {
        issues.push({
          type: 'missing_sim',
          label: 'SIM-Karte fehlt',
          urgency: 'warning',
          details: 'Keine SIM-Karte eingetragen',
        });
        summary.byCategory.missingData++;
      }
    }

    // 10. OFFENE AUFGABEN
    if (contract.tasks && contract.tasks.length > 0) {
      issues.push({
        type: 'open_tasks',
        label: 'Offene Aufgaben',
        urgency: 'ok',
        details: `${contract.tasks.length} offene Aufgabe(n)`,
      });
      summary.byCategory.openTasks++;
    }

    // 11. PENDING STATUS
    if (contract.status === 'PENDING') {
      issues.push({
        type: 'pending_status',
        label: 'Warte auf Aktivierung',
        urgency: 'ok',
        details: 'Vertrag noch nicht aktiv',
      });
      summary.byCategory.pendingContracts++;
    }

    // 12. DRAFT STATUS
    if (contract.status === 'DRAFT') {
      issues.push({
        type: 'draft_status',
        label: 'Entwurf',
        urgency: 'warning',
        details: 'Vertrag ist noch ein Entwurf',
      });
      summary.byCategory.pendingContracts++;
    }

    // 13. ENERGIE-RECHNUNGEN (nur für ELECTRICITY und GAS)
    if (['ELECTRICITY', 'GAS'].includes(contract.type) && contract.energyDetails) {
      const invoices = contract.energyDetails.invoices || [];
      const now = new Date();
      now.setHours(0, 0, 0, 0);

      // 13a. SCHLUSSRECHNUNG FEHLT (nur wenn Vertrag bestätigt gekündigt/deaktiviert)
      // "Beendet" = CANCELLED_CONFIRMED (Kündigung bestätigt) oder DEACTIVATED
      // (nicht nur Laufzeit abgelaufen; NICHT "Bestätigung abwarten"/CANCELLED,
      //  da dort das Vertragsende noch nicht feststeht).
      const isContractTerminated = contract.status === 'CANCELLED_CONFIRMED' || contract.status === 'DEACTIVATED';

      if (isContractTerminated) {
        const hasFinalInvoice = invoices.some(inv => inv.invoiceType === 'FINAL');
        const hasNotAvailable = invoices.some(inv => inv.invoiceType === 'NOT_AVAILABLE');

        if (!hasFinalInvoice && !hasNotAvailable) {
          issues.push({
            type: 'missing_final_invoice',
            label: 'Schlussrechnung fehlt',
            urgency: 'warning',
            details: 'Vertrag gekündigt/deaktiviert, aber keine Schlussrechnung vorhanden',
          });
          summary.byCategory.missingInvoices++;
        }
      }

      // 13b. ZWISCHENRECHNUNG FEHLT/ÜBERFÄLLIG (wenn Vertrag > 12 Monate läuft)
      // Für alle Status außer DRAFT und nicht gekündigt/deaktiviert
      // Auch EXPIRED zählt hier, da der Vertrag ohne Kündigung weiterläuft!
      // ONGOING wird ebenfalls ausgeschlossen – ist ein Fristen-Check.
      if (contract.startDate && contract.status !== 'DRAFT' && !isContractTerminated && !skipDeadlines) {
        const startDate = new Date(contract.startDate);
        startDate.setHours(0, 0, 0, 0);
        const daysSinceStart = Math.floor((now.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));

        if (daysSinceStart > 365) {
          // Vertrag läuft > 12 Monate
          if (invoices.length === 0) {
            // Keine Rechnungen vorhanden
            issues.push({
              type: 'missing_interim_invoice',
              label: 'Zwischenrechnung fehlt',
              urgency: 'warning',
              details: 'Vertrag läuft über 12 Monate ohne Rechnung',
            });
            summary.byCategory.missingInvoices++;
          } else {
            // Prüfen ob letzte Rechnung > 12 Monate alt
            const latestInvoice = invoices
              .filter(inv => inv.invoiceType !== 'NOT_AVAILABLE')
              .sort((a, b) => new Date(b.invoiceDate).getTime() - new Date(a.invoiceDate).getTime())[0];

            if (latestInvoice) {
              const invoiceDate = new Date(latestInvoice.invoiceDate);
              const daysSinceInvoice = Math.floor((now.getTime() - invoiceDate.getTime()) / (1000 * 60 * 60 * 24));

              if (daysSinceInvoice > 365) {
                issues.push({
                  type: 'overdue_interim_invoice',
                  label: 'Zwischenrechnung überfällig',
                  urgency: 'warning',
                  details: `Letzte Rechnung vor ${Math.floor(daysSinceInvoice / 30)} Monaten`,
                });
                summary.byCategory.missingInvoices++;
              }
            }
          }
        }
      }
    }

    // #14 - Consent-Prüfung (nur für aktive Verträge, einmal pro Kunde)
    if (['ACTIVE', 'PENDING', 'DRAFT'].includes(contract.status) && !customerConsentWarned.has(contract.customer.id)) {
      const granted = grantedConsentsMap.get(contract.customer.id);
      const withdrawn = withdrawnConsentsMap.get(contract.customer.id);
      const requiredTypes = ['DATA_PROCESSING', 'MARKETING_EMAIL', 'MARKETING_PHONE', 'DATA_SHARING_PARTNER'];

      if (withdrawn && withdrawn.size > 0) {
        // Mindestens eine Einwilligung widerrufen
        issues.push({
          type: 'consent_withdrawn',
          label: 'Einwilligung widerrufen',
          urgency: 'critical',
          details: `${withdrawn.size} Einwilligung(en) widerrufen`,
        });
        summary.byCategory.missingConsents++;
        customerConsentWarned.add(contract.customer.id);
      } else if (!granted || granted.size < requiredTypes.length) {
        // Nicht alle 4 Einwilligungen erteilt
        const missing = requiredTypes.length - (granted?.size || 0);
        issues.push({
          type: 'missing_consents',
          label: 'Fehlende Einwilligungen',
          urgency: 'critical',
          details: `${missing} von ${requiredTypes.length} Einwilligungen fehlen`,
        });
        summary.byCategory.missingConsents++;
        customerConsentWarned.add(contract.customer.id);
      }
    }

    // Nur Verträge mit Issues hinzufügen
    if (issues.length > 0) {
      const highestUrgency = getHighestUrgency(issues);

      const customerName = contract.customer.companyName ||
        `${contract.customer.firstName} ${contract.customer.lastName}`;

      cockpitContracts.push({
        id: contract.id,
        contractNumber: contract.contractNumber,
        type: contract.type,
        status: contract.status,
        customer: {
          id: contract.customer.id,
          customerNumber: contract.customer.customerNumber,
          name: customerName,
        },
        provider: contract.provider ? {
          id: contract.provider.id,
          name: contract.provider.name,
        } : undefined,
        tariff: contract.tariff ? {
          id: contract.tariff.id,
          name: contract.tariff.name,
        } : undefined,
        providerName: contract.providerName || undefined,
        tariffName: contract.tariffName || undefined,
        issues,
        highestUrgency,
      });

      // Summary zählen
      summary.totalContracts++;
      if (highestUrgency === 'critical') summary.criticalCount++;
      else if (highestUrgency === 'warning') summary.warningCount++;
      else if (highestUrgency === 'ok') summary.okCount++;
    }
  }

  // Sortiere nach Dringlichkeit
  cockpitContracts.sort((a, b) => {
    const urgencyOrder: Record<UrgencyLevel, number> = {
      critical: 0,
      warning: 1,
      ok: 2,
      none: 3,
    };
    return urgencyOrder[a.highestUrgency] - urgencyOrder[b.highestUrgency];
  });

  // Vertragsunabhängige Ausweis-Warnungen
  const documentAlerts = await getDocumentExpiryAlerts(docExpiryCriticalDays, docExpiryWarningDays, opts?.customerIds);

  // Gemeldete Zählerstände (REPORTED Status)
  const reportedReadings = await getReportedMeterReadings(opts?.customerIds);

  // Filter „Kündigungsbestätigung vorhanden": alle Verträge mit
  // cancellationConfirmationPath und/oder -Date in Status ACTIVE/DRAFT/
  // CANCELLED/EXPIRED. Eigene Liste, weil solche Verträge (v.a. bereits
  // CANCELLED/EXPIRED) sonst mangels „Issue" nicht in der Cockpit-Liste
  // auftauchen.
  const CANCEL_CONF_STATUSES: ContractStatus[] = ['ACTIVE', 'DRAFT', 'CANCELLED', 'CANCELLED_CONFIRMED', 'EXPIRED'];
  const cancellationConfirmations: CockpitContract[] = contracts
    .filter(
      (c) =>
        (c.cancellationConfirmationPath || c.cancellationConfirmationDate) &&
        CANCEL_CONF_STATUSES.includes(c.status),
    )
    .map((c) => {
      const customerName =
        c.customer.companyName || `${c.customer.firstName} ${c.customer.lastName}`;
      const dateStr = c.cancellationConfirmationDate
        ? new Date(c.cancellationConfirmationDate).toLocaleDateString('de-DE')
        : null;
      const parts: string[] = [];
      if (c.cancellationConfirmationPath) parts.push('Dokument');
      if (dateStr) parts.push(`Datum ${dateStr}`);
      return {
        id: c.id,
        contractNumber: c.contractNumber,
        type: c.type,
        status: c.status,
        customer: {
          id: c.customer.id,
          customerNumber: c.customer.customerNumber,
          name: customerName,
        },
        provider: c.provider ? { id: c.provider.id, name: c.provider.name } : undefined,
        tariff: c.tariff ? { id: c.tariff.id, name: c.tariff.name } : undefined,
        providerName: c.providerName || undefined,
        tariffName: c.tariffName || undefined,
        issues: [
          {
            type: 'has_cancellation_confirmation',
            label: 'Kündigungsbestätigung vorhanden',
            urgency: 'none' as UrgencyLevel,
            details: parts.join(' · ') || 'Kündigungsbestätigung hinterlegt',
          },
        ],
        highestUrgency: 'none' as UrgencyLevel,
      };
    });

  return {
    contracts: cockpitContracts,
    cancellationConfirmations,
    documentAlerts,
    reportedReadings,
    summary,
    thresholds: {
      criticalDays,
      warningDays,
      okDays,
    },
  };
}

/**
 * Alle aktiven Ausweise die ablaufen oder abgelaufen sind (vertragsunabhängig)
 */
async function getDocumentExpiryAlerts(
  criticalDays: number,
  warningDays: number,
  customerIds?: number[],
): Promise<DocumentAlert[]> {
  const now = new Date();
  const inWarningDays = new Date(now.getTime() + warningDays * 24 * 60 * 60 * 1000);

  const documents = await prisma.identityDocument.findMany({
    where: {
      isActive: true,
      expiryDate: { lte: inWarningDays },
      ...(customerIds ? { customerId: { in: customerIds } } : {}),
    },
    include: {
      customer: {
        select: { id: true, customerNumber: true, firstName: true, lastName: true },
      },
    },
    orderBy: { expiryDate: 'asc' },
  });

  return documents.map((doc) => {
    const expiryDate = new Date(doc.expiryDate!);
    const daysUntilExpiry = Math.ceil((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));

    let urgency: UrgencyLevel = 'warning';
    if (daysUntilExpiry < 0) urgency = 'critical';
    else if (daysUntilExpiry <= criticalDays) urgency = 'critical';

    return {
      id: doc.id,
      type: doc.type,
      documentNumber: doc.documentNumber,
      expiryDate: expiryDate.toISOString(),
      daysUntilExpiry,
      urgency,
      customer: {
        id: doc.customer.id,
        customerNumber: doc.customer.customerNumber,
        name: `${doc.customer.firstName} ${doc.customer.lastName}`,
      },
    };
  });
}

/**
 * Vom Kunden gemeldete Zählerstände die noch nicht übertragen wurden
 */
async function getReportedMeterReadings(customerIds?: number[]): Promise<ReportedMeterReading[]> {
  const readings = await prisma.meterReading.findMany({
    where: {
      status: 'REPORTED',
      ...(customerIds ? { meter: { customerId: { in: customerIds } } } : {}),
    },
    include: {
      meter: {
        include: {
          customer: {
            select: { id: true, customerNumber: true, firstName: true, lastName: true },
          },
          // Energie-Verträge für diesen Zähler (um Provider-Portal-Daten zu bekommen)
          energyDetails: {
            include: {
              contract: {
                select: {
                  id: true,
                  contractNumber: true,
                  portalUsername: true,
                  provider: {
                    select: { id: true, name: true, portalUrl: true },
                  },
                },
              },
            },
            take: 1,
          },
        },
      },
    },
    orderBy: { createdAt: 'asc' },
  });

  return readings.map((r) => {
    const contract = r.meter.energyDetails?.[0]?.contract;
    const provider = contract?.provider;

    return {
      id: r.id,
      readingDate: r.readingDate.toISOString(),
      value: r.value,
      unit: r.unit,
      notes: r.notes ?? undefined,
      reportedBy: r.reportedBy ?? undefined,
      createdAt: r.createdAt.toISOString(),
      meter: {
        id: r.meter.id,
        meterNumber: r.meter.meterNumber,
        type: r.meter.type,
      },
      customer: {
        id: r.meter.customer.id,
        customerNumber: r.meter.customer.customerNumber,
        name: `${r.meter.customer.firstName} ${r.meter.customer.lastName}`,
      },
      contract: contract ? {
        id: contract.id,
        contractNumber: contract.contractNumber,
      } : undefined,
      providerPortal: provider?.portalUrl ? {
        providerName: provider.name,
        portalUrl: provider.portalUrl,
        portalUsername: contract?.portalUsername ?? undefined,
      } : undefined,
    };
  });
}
