import type { MeterReading, ContractMeter } from '../types';

// Konstante für Gas → kWh Umrechnung (Standard Erdgas H)
export const GAS_TO_KWH_FACTOR = 10.5;

// Ergebnis der Verbrauchsberechnung
export interface ConsumptionCalculation {
  type: 'exact' | 'projected' | 'insufficient' | 'none';
  consumptionM3?: number;      // Nur bei Gas
  consumptionKwh: number;
  consumptionHt?: number;      // Nur bei HT/NT: HT-Verbrauch in kWh
  consumptionNt?: number;      // Nur bei HT/NT: NT-Verbrauch in kWh
  startReading?: MeterReading;
  endReading?: MeterReading;
  projectedEndDate?: string;   // Nur bei Hochrechnung
  message?: string;
}

// Ergebnis der Kostenberechnung
export interface CostCalculation {
  annualBaseCost: number;        // basePrice × 12
  annualConsumptionCost: number; // verbrauch × unitPrice (HT bei Zweitarif)
  annualConsumptionCostNt?: number; // NT-Verbrauch × unitPriceNt
  annualTotalCost: number;       // Summe
  monthlyPayment: number;        // annualTotalCost / 12
  instantBonus?: number;         // Sofort-Bonus
  newCustomerBonus?: number;     // Neukunden-Bonus
  totalBonus?: number;           // Summe = instantBonus + newCustomerBonus
  effectiveAnnualCost: number;   // annualTotalCost - totalBonus
}

/**
 * Berechnet die Differenz in Tagen zwischen zwei Daten
 */
function daysDiff(startDate: string, endDate: string): number {
  const start = new Date(startDate);
  const end = new Date(endDate);
  start.setHours(0, 0, 0, 0);
  end.setHours(0, 0, 0, 0);
  const diffTime = end.getTime() - start.getTime();
  return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
}

/**
 * Filtert Zählerstände nach Vertragszeitraum
 * Ein Zählerstand gilt als "im Zeitraum" wenn:
 * readingDate >= startDate UND readingDate <= endDate
 */
export function filterReadingsByContractPeriod(
  readings: MeterReading[],
  startDate: string,
  endDate: string
): MeterReading[] {
  const start = new Date(startDate);
  const end = new Date(endDate);
  start.setHours(0, 0, 0, 0);
  end.setHours(0, 0, 0, 0);

  return readings.filter((reading) => {
    const readingDate = new Date(reading.readingDate);
    readingDate.setHours(0, 0, 0, 0);
    return readingDate >= start && readingDate <= end;
  });
}

/**
 * Berechnet den Verbrauch basierend auf den Zählerständen
 *
 * Fälle:
 * A) Anfangs- UND Endzählerstand vorhanden -> exakter Verbrauch
 * B) >= 2 Stände, kein Endzählerstand -> Hochrechnung
 * C) Nur 1 Stand -> insufficient
 * D) Keine Stände -> none
 */
export function calculateConsumption(
  readings: MeterReading[],
  startDate: string,
  endDate: string,
  contractType: 'ELECTRICITY' | 'GAS'
): ConsumptionCalculation {
  const filteredReadings = filterReadingsByContractPeriod(readings, startDate, endDate);

  // Fall D: Keine Zählerstände
  if (filteredReadings.length === 0) {
    return { type: 'none', consumptionKwh: 0 };
  }

  // Fall C: Nur 1 Zählerstand
  if (filteredReadings.length === 1) {
    return {
      type: 'insufficient',
      consumptionKwh: 0,
      message: 'Berechnung auf Grund fehlender Stände nicht möglich',
    };
  }

  // Sortieren nach Datum (älteste zuerst)
  const sorted = [...filteredReadings].sort(
    (a, b) => new Date(a.readingDate).getTime() - new Date(b.readingDate).getTime()
  );

  const lastReading = sorted[sorted.length - 1];

  // Prüfen ob Endzählerstand am/nach Vertragsende liegt
  const lastReadingDate = new Date(lastReading.readingDate);
  const contractEndDate = new Date(endDate);
  lastReadingDate.setHours(0, 0, 0, 0);
  contractEndDate.setHours(0, 0, 0, 0);

  // Verbrauch zwischen aufeinanderfolgenden Ständen berechnen
  // (Erkennt Zählerwechsel: wenn ein Wert sinkt, wird ab dem neuen Stand weitergerechnet)
  let totalConsumption = 0;
  let totalConsumptionNt = 0;
  const hasNt = sorted.some(r => r.valueNt !== undefined && r.valueNt !== null);
  let effectiveFirstReading = sorted[0];
  for (let i = 1; i < sorted.length; i++) {
    const diff = sorted[i].value - sorted[i - 1].value;
    if (diff < 0) {
      totalConsumption = 0;
      totalConsumptionNt = 0;
      effectiveFirstReading = sorted[i];
    } else {
      totalConsumption += diff;
      if (hasNt && sorted[i].valueNt != null && sorted[i - 1].valueNt != null) {
        const diffNt = sorted[i].valueNt! - sorted[i - 1].valueNt!;
        if (diffNt >= 0) totalConsumptionNt += diffNt;
      }
    }
  }

  const effectiveLastReading = sorted[sorted.length - 1];

  // Fall A: Exakter Verbrauch
  if (lastReadingDate >= contractEndDate) {
    const result = formatConsumptionResult('exact', totalConsumption, contractType, effectiveFirstReading, effectiveLastReading);
    if (hasNt) { result.consumptionHt = totalConsumption; result.consumptionNt = totalConsumptionNt; }
    return result;
  }

  // Fall B: Hochrechnung erforderlich
  const daysBetweenReadings = daysDiff(effectiveFirstReading.readingDate, effectiveLastReading.readingDate);

  if (daysBetweenReadings < 1) {
    return {
      type: 'insufficient',
      consumptionKwh: 0,
      message: 'Zeitraum zwischen Zählerständen zu kurz für Berechnung',
    };
  }

  const totalContractDays = daysDiff(startDate, endDate);
  const projectedConsumption = (totalConsumption / daysBetweenReadings) * totalContractDays;

  const result = formatConsumptionResult(
    'projected',
    projectedConsumption,
    contractType,
    effectiveFirstReading,
    effectiveLastReading,
    endDate
  );

  if (hasNt) {
    const projectedNt = (totalConsumptionNt / daysBetweenReadings) * totalContractDays;
    result.consumptionHt = projectedConsumption;
    result.consumptionNt = projectedNt;
    // Gesamt-kWh = HT + NT
    result.consumptionKwh = projectedConsumption + projectedNt;
  }

  return result;
}

/**
 * Formatiert das Ergebnis der Verbrauchsberechnung
 */
function formatConsumptionResult(
  type: 'exact' | 'projected',
  consumption: number,
  contractType: 'ELECTRICITY' | 'GAS',
  startReading: MeterReading,
  endReading: MeterReading,
  projectedEndDate?: string
): ConsumptionCalculation {
  if (contractType === 'GAS') {
    return {
      type,
      consumptionM3: consumption,
      consumptionKwh: consumption * GAS_TO_KWH_FACTOR,
      startReading,
      endReading,
      projectedEndDate,
    };
  }
  return {
    type,
    consumptionKwh: consumption,
    startReading,
    endReading,
    projectedEndDate,
  };
}

/**
 * Berechnet die Kosten basierend auf Verbrauch und Preisen
 * Gibt null zurück wenn keine Preise vorhanden sind
 */
export function calculateCosts(
  consumptionKwh: number,
  basePrice?: number,
  unitPrice?: number,
  instantBonus?: number,
  consumptionNtKwh?: number,
  unitPriceNt?: number,
  newCustomerBonus?: number,
): CostCalculation | null {
  // Mindestens ein Preis muss vorhanden sein
  if (basePrice == null && unitPrice == null) {
    return null;
  }

  const annualBaseCost = (basePrice ?? 0) * 12;
  // Bei HT/NT: consumptionKwh ist nur HT, NT wird separat berechnet
  const annualConsumptionCost = consumptionKwh * (unitPrice ?? 0);
  const annualConsumptionCostNt = (consumptionNtKwh ?? 0) * (unitPriceNt ?? 0);
  const annualTotalCost = annualBaseCost + annualConsumptionCost + annualConsumptionCostNt;
  const totalBonus = (instantBonus ?? 0) + (newCustomerBonus ?? 0);
  const effectiveAnnualCost = annualTotalCost - totalBonus;
  const monthlyPayment = effectiveAnnualCost / 12;

  return {
    annualBaseCost,
    annualConsumptionCost,
    annualConsumptionCostNt: annualConsumptionCostNt > 0 ? annualConsumptionCostNt : undefined,
    annualTotalCost,
    monthlyPayment,
    instantBonus: instantBonus && instantBonus > 0 ? instantBonus : undefined,
    newCustomerBonus: newCustomerBonus && newCustomerBonus > 0 ? newCustomerBonus : undefined,
    totalBonus: totalBonus > 0 ? totalBonus : undefined,
    effectiveAnnualCost,
  };
}

/**
 * Berechnet den Verbrauch über mehrere Zähler (Folgezähler).
 * Pro Zähler wird der Verbrauch einzeln berechnet und dann summiert.
 */
export function calculateMultiMeterConsumption(
  contractMeters: ContractMeter[],
  startDate: string,
  endDate: string,
  contractType: 'ELECTRICITY' | 'GAS'
): ConsumptionCalculation {
  if (contractMeters.length === 0) {
    return { type: 'none', consumptionKwh: 0 };
  }

  let totalConsumption = 0;
  let totalConsumptionM3 = 0;
  let hasExact = true;
  let hasAny = false;
  let firstStart: MeterReading | undefined;
  let lastEnd: MeterReading | undefined;

  const contractStartMs = new Date(startDate).getTime();
  const contractEndMs = new Date(endDate).getTime();

  for (const cm of contractMeters) {
    const readings = cm.meter?.readings || [];
    if (readings.length === 0) continue;

    // Zeitraum für diesen Zähler bestimmen, GE-CLAMPED auf die Vertragslaufzeit.
    // Ohne Clamp würden Folgezähler, die nach Vertragsende installiert wurden
    // (typisch bei Vorgängerverträgen einer Folgevertrags-Kette), zukünftige
    // Zählerstände in den Verbrauch dieses Vertrags einrechnen.
    const installedMs = cm.installedAt ? new Date(cm.installedAt).getTime() : contractStartMs;
    const removedMs = cm.removedAt ? new Date(cm.removedAt).getTime() : contractEndMs;
    const meterStartMs = Math.max(installedMs, contractStartMs);
    const meterEndMs = Math.min(removedMs, contractEndMs);
    if (meterStartMs > meterEndMs) continue; // Zähler liegt komplett außerhalb der Laufzeit
    const meterStart = new Date(meterStartMs).toISOString();
    const meterEnd = new Date(meterEndMs).toISOString();

    const result = calculateConsumption(readings, meterStart, meterEnd, contractType);

    if (result.type === 'none' || result.type === 'insufficient') continue;

    hasAny = true;
    if (result.type === 'projected') hasExact = false;

    totalConsumption += result.consumptionKwh;
    if (result.consumptionM3) totalConsumptionM3 += result.consumptionM3;

    if (!firstStart && result.startReading) firstStart = result.startReading;
    if (result.endReading) lastEnd = result.endReading;
  }

  if (!hasAny) {
    // Fallback: Einzelzähler-Berechnung mit allen Readings
    const allReadings = contractMeters.flatMap(cm => cm.meter?.readings || []);
    return calculateConsumption(allReadings, startDate, endDate, contractType);
  }

  return {
    type: hasExact ? 'exact' : 'projected',
    consumptionKwh: totalConsumption,
    consumptionM3: contractType === 'GAS' ? totalConsumptionM3 : undefined,
    startReading: firstStart,
    endReading: lastEnd,
  };
}
