import type { AppState } from '@domain/ports/StateRepository';
import { RACE_GEN } from '@domain/value-objects/Race';

// 66 races totales moins 3 Gen 1 (Rousse, Dorée, Amande) qui se capturent et ne se créent pas
export const TOTAL_RACES = 63;

export interface GetStatisticsQuery {
  type: 'get-statistics';
  days?: number;  // 0 = tout l'historique
}

export interface DailyBirths {
  date: string;
  label: string;
  count: number;
}

export interface RaceShare {
  race: string;
  count: number;
  pct: number;
}

export interface KpiDelta {
  value: number;
  delta: number | null;
}

export interface RaceSuccessRate {
  race: string;
  couples: number;
  babies: number;
  rate: number;
}

export interface BestCouple {
  parentA: string;
  parentB: string;
  baby: string;
  couples: number;
  babies: number;
  rate: number;
}

export interface GenBreakdown {
  gen: number;
  babies: number;
  couples: number;
  races: number;
}

export interface MissingRace {
  name: string;
  gen: number;
}

export interface WeekdayActivity {
  day: string;
  count: number;
}

export interface StatisticsResult {
  totalBabies: KpiDelta;
  totalCouples: KpiDelta;
  successRate: KpiDelta;
  racesCount: KpiDelta;
  dailyBirths: DailyBirths[];
  raceShares: RaceShare[];
  raceSuccessRates: RaceSuccessRate[];
  bestCouples: BestCouple[];
  genBreakdown: GenBreakdown[];
  missingRaces: MissingRace[];
  weekdayActivity: WeekdayActivity[];
  days: number;
}

interface AccEntry {
  parentA: string;
  parentB: string;
  baby: string;
  gen: number;
  couples: number;
  babiesObtained: number;
  date: string;
}

function toISO(d: Date): string {
  return d.toISOString().slice(0, 10);
}

function aggregate(entries: AccEntry[]) {
  let couples = 0, babies = 0;
  const races = new Set<string>();
  for (const e of entries) {
    couples += e.couples;
    babies += e.babiesObtained;
    if (e.babiesObtained > 0) races.add(e.baby);
  }
  const rate = couples > 0 ? Math.round((babies / couples) * 100) : 0;
  return { couples, babies, rate, racesCount: races.size };
}

const WEEKDAY_NAMES = ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'];

export function createGetStatisticsHandler(state: AppState) {
  return (query: GetStatisticsQuery): StatisticsResult => {
    const days = query.days ?? 30;

    // Collecter toutes les entrées normalisées (exclure Gen 1 : non créables, seulement capturables)
    const all: AccEntry[] = [];
    for (const acc of state.accouplements) {
      if (acc.gen === 1 || (RACE_GEN[acc.baby] ?? 0) === 1) continue;
      all.push({
        parentA: acc.parentA, parentB: acc.parentB,
        baby: acc.baby, gen: acc.gen,
        couples: acc.couples, babiesObtained: acc.babiesObtained,
        date: acc.date,
      });
    }
    for (const arch of state.archivedStats as Array<{
      parentA?: string; parentB?: string; baby?: string; gen?: number;
      couples?: number; babiesObtained?: number; date?: string;
    }>) {
      if (arch.baby) {
        const gen = arch.gen ?? (RACE_GEN[arch.baby] ?? 0);
        if (gen === 1) continue;
        all.push({
          parentA: arch.parentA ?? '', parentB: arch.parentB ?? '',
          baby: arch.baby, gen,
          couples: arch.couples ?? 0,
          babiesObtained: arch.babiesObtained ?? 0,
          date: arch.date ?? '',
        });
      }
    }

    const now = new Date();
    const todayISO = toISO(now);

    let current: AccEntry[];
    let previous: AccEntry[] | null;

    if (days === 0) {
      current = all;
      previous = null;
    } else {
      const startCurrent = new Date(now);
      startCurrent.setDate(startCurrent.getDate() - days);
      const startCurrentISO = toISO(startCurrent);

      const startPrevious = new Date(startCurrent);
      startPrevious.setDate(startPrevious.getDate() - days);
      const startPreviousISO = toISO(startPrevious);

      current = all.filter(e => {
        const d = e.date.slice(0, 10);
        return d >= startCurrentISO && d <= todayISO;
      });
      previous = all.filter(e => {
        const d = e.date.slice(0, 10);
        return d >= startPreviousISO && d < startCurrentISO;
      });
    }

    const cur = aggregate(current);
    const prev = previous ? aggregate(previous) : null;

    function delta(curVal: number, prevVal: number | null): KpiDelta {
      if (prevVal === null) return { value: curVal, delta: null };
      return { value: curVal, delta: curVal - prevVal };
    }

    // ── Naissances par jour ──────────────────────────────────────
    const dailyMap: Record<string, number> = {};
    for (const e of current) {
      const day = e.date.slice(0, 10);
      if (day) dailyMap[day] = (dailyMap[day] ?? 0) + e.babiesObtained;
    }
    const chartDays = days === 0 ? 30 : days;
    const dailyBirths: DailyBirths[] = [];
    for (let i = chartDays - 1; i >= 0; i--) {
      const d = new Date(now);
      d.setDate(d.getDate() - i);
      const iso = toISO(d);
      const label = `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}`;
      dailyBirths.push({ date: iso, label, count: dailyMap[iso] ?? 0 });
    }

    // ── Répartition des races ────────────────────────────────────
    const raceBreakdown: Record<string, number> = {};
    let totalBabiesForShares = 0;
    for (const e of current) {
      raceBreakdown[e.baby] = (raceBreakdown[e.baby] ?? 0) + e.babiesObtained;
      totalBabiesForShares += e.babiesObtained;
    }
    const raceShares: RaceShare[] = Object.entries(raceBreakdown)
      .sort((a, b) => b[1] - a[1])
      .map(([race, count]) => ({
        race, count,
        pct: totalBabiesForShares > 0 ? Math.round((count / totalBabiesForShares) * 100) : 0,
      }));

    // ── Taux de réussite par race ────────────────────────────────
    const raceAgg: Record<string, { couples: number; babies: number }> = {};
    for (const e of current) {
      if (!raceAgg[e.baby]) raceAgg[e.baby] = { couples: 0, babies: 0 };
      raceAgg[e.baby].couples += e.couples;
      raceAgg[e.baby].babies += e.babiesObtained;
    }
    const raceSuccessRates: RaceSuccessRate[] = Object.entries(raceAgg)
      .map(([race, { couples, babies }]) => ({
        race, couples, babies,
        rate: couples > 0 ? Math.round((babies / couples) * 100) : 0,
      }))
      .sort((a, b) => b.rate - a.rate);

    // ── Meilleurs couples ────────────────────────────────────────
    const coupleAgg: Record<string, { parentA: string; parentB: string; baby: string; couples: number; babies: number }> = {};
    for (const e of current) {
      if (!e.parentA || !e.parentB) continue;
      // Clé normalisée (ordre alphabétique) pour éviter les doublons A+B / B+A
      const key = [e.parentA, e.parentB].sort().join('|');
      if (!coupleAgg[key]) {
        coupleAgg[key] = { parentA: e.parentA, parentB: e.parentB, baby: e.baby, couples: 0, babies: 0 };
      }
      coupleAgg[key].couples += e.couples;
      coupleAgg[key].babies += e.babiesObtained;
    }
    const bestCouples: BestCouple[] = Object.values(coupleAgg)
      .map(c => ({ ...c, rate: c.couples > 0 ? Math.round((c.babies / c.couples) * 100) : 0 }))
      .sort((a, b) => b.rate - a.rate || b.babies - a.babies)
      .slice(0, 10);

    // ── Répartition par génération ───────────────────────────────
    const genAgg: Record<number, { babies: number; couples: number; races: Set<string> }> = {};
    for (const e of current) {
      const g = e.gen || (RACE_GEN[e.baby] ?? 0);
      if (!g) continue;
      if (!genAgg[g]) genAgg[g] = { babies: 0, couples: 0, races: new Set() };
      genAgg[g].babies += e.babiesObtained;
      genAgg[g].couples += e.couples;
      if (e.babiesObtained > 0) genAgg[g].races.add(e.baby);
    }
    const genBreakdown: GenBreakdown[] = Object.entries(genAgg)
      .map(([g, v]) => ({ gen: Number(g), babies: v.babies, couples: v.couples, races: v.races.size }))
      .sort((a, b) => a.gen - b.gen);

    // ── Races manquantes ─────────────────────────────────────────
    const obtainedAll = new Set<string>();
    for (const e of all) {
      if (e.babiesObtained > 0) obtainedAll.add(e.baby);
    }
    const missingRaces: MissingRace[] = Object.entries(RACE_GEN)
      .filter(([name, gen]) => gen !== 1 && !obtainedAll.has(name))
      .map(([name, gen]) => ({ name, gen }))
      .sort((a, b) => a.gen - b.gen || a.name.localeCompare(b.name));

    // ── Activité par jour de la semaine ──────────────────────────
    const weekdayCounts = [0, 0, 0, 0, 0, 0, 0];
    for (const e of current) {
      const d = e.date.slice(0, 10);
      if (!d) continue;
      const dt = new Date(d + 'T12:00:00');
      weekdayCounts[dt.getDay()] += e.babiesObtained;
    }
    // Réordonner : Lundi → Dimanche
    const weekdayActivity: WeekdayActivity[] = [];
    for (let i = 1; i <= 7; i++) {
      const idx = i % 7;
      weekdayActivity.push({ day: WEEKDAY_NAMES[idx], count: weekdayCounts[idx] });
    }

    return {
      totalBabies: delta(cur.babies, prev?.babies ?? null),
      totalCouples: delta(cur.couples, prev?.couples ?? null),
      successRate: delta(cur.rate, prev?.rate ?? null),
      racesCount: delta(cur.racesCount, prev?.racesCount ?? null),
      dailyBirths,
      raceShares,
      raceSuccessRates,
      bestCouples,
      genBreakdown,
      missingRaces,
      weekdayActivity,
      days,
    };
  };
}
