import { Injectable } from '@nestjs/common';
import { ANALYTICS_PERIODS, type AnalyticsPeriod, type AnalyticsSummary } from '@siop/shared';
import { PrismaService } from '../prisma/prisma.service';

const debutMois = (decalage: number): Date => {
  const d = new Date();
  return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() - decalage, 1));
};

/** Le tableau de la direction — tout est DÉRIVÉ des données réelles :
 *  mouvements (prix figés), main-d'œuvre (taux figés), bilans codés. */
@Injectable()
export class AnalyticsService {
  constructor(private readonly prisma: PrismaService) {}

  async summary(monthsRaw?: string): Promise<AnalyticsSummary> {
    // Période demandée par l'écran (3, 6 ou 12 mois) — 12 par défaut.
    const months: AnalyticsPeriod = ANALYTICS_PERIODS.find(
      (p) => p === Number(monthsRaw),
    ) ?? 12;
    const depuisPeriode = debutMois(months - 1);

    const [consommations, mainOeuvre, clos12m, grilles12m, correctifsClos, pannes, topDonnees] =
      await Promise.all([
        this.prisma.stockMovement.findMany({
          where: { kind: 'CONSUMPTION', createdAt: { gte: depuisPeriode } },
          select: { quantity: true, unitPrice: true, createdAt: true },
        }),
        this.prisma.laborTime.findMany({
          where: { createdAt: { gte: depuisPeriode } },
          select: { minutes: true, hourlyRate: true, createdAt: true },
        }),
        this.prisma.workOrder.groupBy({
          by: ['type'],
          where: { status: 'DONE', completedAt: { gte: depuisPeriode } },
          _count: { _all: true },
        }),
        this.prisma.workOrder.findMany({
          where: { periodKey: { not: null }, createdAt: { gte: depuisPeriode } },
          select: { status: true },
        }),
        this.prisma.workOrder.findMany({
          where: { type: 'CORRECTIVE', status: 'DONE', completedAt: { gte: depuisPeriode } },
          select: { createdAt: true, completedAt: true },
        }),
        this.prisma.interventionReport.groupBy({
          by: ['componentConcernedId'],
          where: {
            componentConcernedId: { not: null },
            workOrder: { status: 'DONE', type: 'CORRECTIVE', completedAt: { gte: depuisPeriode } },
          },
          _count: { _all: true },
        }),
        this.prisma.stockMovement.findMany({
          where: { kind: 'CONSUMPTION', workOrderId: { not: null }, createdAt: { gte: depuisPeriode } },
          select: {
            quantity: true,
            unitPrice: true,
            workOrder: {
              select: {
                type: true,
                asset: {
                  select: {
                    reference: true,
                    location: { select: { name: true, parent: { select: { name: true } } } },
                  },
                },
              },
            },
          },
        }),
      ]);

    // Coûts par mois (6 derniers) — pièces + main-d'œuvre, prix/taux figés
    const cleMois = (d: Date) => d.toISOString().slice(0, 7);
    const parMois = new Map<string, number>();
    for (let i = months - 1; i >= 0; i--) parMois.set(cleMois(debutMois(i)), 0);
    for (const c of consommations) {
      const cle = cleMois(c.createdAt);
      if (parMois.has(cle)) {
        parMois.set(cle, parMois.get(cle)! + -c.quantity * Number(c.unitPrice ?? 0));
      }
    }
    for (const l of mainOeuvre) {
      const cle = cleMois(l.createdAt);
      if (parMois.has(cle)) {
        parMois.set(cle, parMois.get(cle)! + (l.minutes / 60) * Number(l.hourlyRate));
      }
    }
    const costsByMonth = [...parMois.entries()].map(([month, total]) => ({
      month,
      total: Math.round(total * 100) / 100,
    }));
    const moisCourant = cleMois(new Date());
    const moisPrecedent = cleMois(debutMois(1));

    // Pannes par organe : depuis les bilans codés
    const labels = await this.prisma.referenceValue.findMany({
      where: { id: { in: pannes.map((p) => p.componentConcernedId!).filter(Boolean) } },
    });
    const labelDe = new Map(labels.map((l) => [l.id, l.label]));
    const failuresByComponent = pannes
      .map((p) => ({
        label: labelDe.get(p.componentConcernedId!) ?? '—',
        count: p._count._all,
      }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 6);

    // Top équipements en coût (pièces via OT + main-d'œuvre)
    const laborParOT = await this.prisma.laborTime.findMany({
      where: { createdAt: { gte: depuisPeriode } },
      select: {
        minutes: true,
        hourlyRate: true,
        workOrder: {
          select: {
            type: true,
            asset: {
              select: {
                reference: true,
                location: { select: { name: true, parent: { select: { name: true } } } },
              },
            },
          },
        },
      },
    });
    interface Cumul { siteName: string; correctives: number; partsCost: number; laborCost: number }
    const parAppareil = new Map<string, Cumul>();
    const cumul = (ref: string, siteName: string): Cumul => {
      if (!parAppareil.has(ref)) {
        parAppareil.set(ref, { siteName, correctives: 0, partsCost: 0, laborCost: 0 });
      }
      return parAppareil.get(ref)!;
    };
    for (const m of topDonnees) {
      const asset = m.workOrder!.asset;
      const c = cumul(asset.reference, asset.location.parent?.name ?? asset.location.name);
      c.partsCost += -m.quantity * Number(m.unitPrice ?? 0);
    }
    for (const l of laborParOT) {
      const asset = l.workOrder.asset;
      const c = cumul(asset.reference, asset.location.parent?.name ?? asset.location.name);
      c.laborCost += (l.minutes / 60) * Number(l.hourlyRate);
    }
    const correctivesParAppareil = await this.prisma.workOrder.groupBy({
      by: ['assetId'],
      where: { type: 'CORRECTIVE', createdAt: { gte: depuisPeriode } },
      _count: { _all: true },
    });
    const assetsRefs = await this.prisma.asset.findMany({
      where: { id: { in: correctivesParAppareil.map((c) => c.assetId) } },
      select: { id: true, reference: true },
    });
    const refDe = new Map(assetsRefs.map((a) => [a.id, a.reference]));
    for (const c of correctivesParAppareil) {
      const ref = refDe.get(c.assetId);
      if (ref && parAppareil.has(ref)) parAppareil.get(ref)!.correctives = c._count._all;
    }
    const topAssets = [...parAppareil.entries()]
      .map(([reference, c]) => ({
        reference,
        siteName: c.siteName,
        correctives: c.correctives,
        partsCost: Math.round(c.partsCost * 100) / 100,
        laborCost: Math.round(c.laborCost * 100) / 100,
        total: Math.round((c.partsCost + c.laborCost) * 100) / 100,
      }))
      .sort((a, b) => b.total - a.total)
      .slice(0, 5);

    const totalClos = clos12m.reduce((s, g) => s + g._count._all, 0);
    const preventifsClos = clos12m.find((g) => g.type === 'PREVENTIVE')?._count._all ?? 0;
    const grillesTerminees = grilles12m.filter((g) => g.status === 'DONE').length;

    return {
      months,
      monthCost: costsByMonth.find((c) => c.month === moisCourant)?.total ?? 0,
      previousMonthCost: costsByMonth.find((c) => c.month === moisPrecedent)?.total ?? 0,
      closed: { total: totalClos, preventive: preventifsClos },
      preventiveRate: grilles12m.length
        ? Math.round((grillesTerminees / grilles12m.length) * 100) / 100
        : null,
      avgResolutionDays: correctifsClos.length
        ? Math.round(
            (correctifsClos.reduce(
              (s, w) => s + (w.completedAt!.getTime() - w.createdAt.getTime()),
              0,
            ) /
              correctifsClos.length /
              86400e3) *
              10,
          ) / 10
        : null,
      failuresByComponent,
      costsByMonth,
      topAssets,
    };
  }
}
