import "server-only";
import type { Prisma, StatutCommercial } from "@prisma/client";
import { db } from "./db";
import { COMMERCIAUX_ACTIFS, COMMERCIAUX_PERDUS, COMMERCIAUX_PIPELINE } from "./libelles";
import { ajouterJours, debutJour } from "./procedure";

export const SECTIONS = ["A", "B", "C", "D", "E", "F", "G"] as const;
export const NOMS_SECTIONS: Record<string, string> = {
  A: "Ouverture",
  B: "Qualification & repérage",
  C: "Proposition commerciale",
  D: "Planification",
  E: "Exploitation",
  F: "Pré-clôture",
  G: "Clôture & archivage",
};

/** État d'une section de procédure, pour les voyants d'avancement. */
export type EtatSection = "termine" | "retard" | "en_cours" | "a_venir";

export function filtreSociete(code?: string): Prisma.DossierWhereInput {
  return code ? { societe: { code } } : {};
}

/** Dossier « vivant » : ni perdu/annulé/reporté, ni clôturé. */
export function filtreDossiersActifs(code?: string): Prisma.DossierWhereInput {
  return {
    ...filtreSociete(code),
    statutCommercial: { in: COMMERCIAUX_ACTIFS },
    statutOperationnel: { not: "CLOTURE" },
  };
}

type TacheResume = { section: string; statut: string; echeance: Date | null };

export function etatsSections(taches: TacheResume[], aujourdHui: Date) {
  const etats: Record<string, { etat: EtatSection; faites: number; total: number; retard: number }> = {};
  for (const s of SECTIONS) {
    const ts = taches.filter((t) => t.section === s);
    const faites = ts.filter((t) => t.statut !== "A_FAIRE").length;
    const retard = ts.filter((t) => t.statut === "A_FAIRE" && t.echeance && t.echeance < aujourdHui).length;
    const etat: EtatSection =
      ts.length > 0 && faites === ts.length ? "termine" : retard > 0 ? "retard" : faites > 0 ? "en_cours" : "a_venir";
    etats[s] = { etat, faites, total: ts.length, retard };
  }
  return etats;
}

export async function donneesTableauDeBord(societe?: string) {
  const aujourdHui = debutJour();
  const dansSeptJours = ajouterJours(aujourdHui, 7);
  const dansTrenteJours = ajouterJours(aujourdHui, 30);
  const actifs = filtreDossiersActifs(societe);
  const tacheActive: Prisma.TacheDossierWhereInput = { statut: "A_FAIRE", dossier: actifs };

  const [
    nbActifs,
    retards,
    echeancesDuJour,
    confirmesAVenir,
    pipeline,
    historique,
    finances,
    devisSansReponse,
    dossiersEnCours,
    semaine,
  ] = await Promise.all([
    db.dossier.count({ where: actifs }),
    db.tacheDossier.findMany({
      where: { ...tacheActive, echeance: { lt: aujourdHui } },
      select: { responsable: true, assigne: { select: { nom: true, couleur: true } } },
    }),
    db.tacheDossier.count({ where: { ...tacheActive, echeance: aujourdHui } }),
    db.dossier.aggregate({
      where: { ...actifs, statutCommercial: "CONFIRME", dateDebut: { gte: aujourdHui, lte: dansTrenteJours } },
      _count: true,
      _sum: { montantEstime: true },
    }),
    db.dossier.groupBy({
      by: ["statutCommercial"],
      where: { ...filtreSociete(societe), statutCommercial: { in: COMMERCIAUX_PIPELINE } },
      _count: true,
      _sum: { montantEstime: true },
    }),
    db.dossier.groupBy({
      by: ["statutCommercial"],
      where: { ...filtreSociete(societe), statutCommercial: { in: ["CONFIRME", ...COMMERCIAUX_PERDUS] } },
      _count: true,
    }),
    db.dossier.groupBy({
      by: ["statutFinancier"],
      where: { ...filtreSociete(societe), statutFinancier: { in: ["IMPAYE", "A_FACTURER_PROFORMA", "A_FACTURER_FINALE", "A_REGULARISER"] } },
      _count: true,
      _sum: { montantEstime: true },
    }),
    db.dossier.count({
      where: { ...filtreSociete(societe), statutCommercial: { in: ["DEVIS_ENVOYE", "EN_INSTANCE"] }, updatedAt: { lt: ajouterJours(aujourdHui, -7) } },
    }),
    db.dossier.findMany({
      where: { ...actifs, taches: { some: {} } },
      orderBy: [{ dateDebut: { sort: "asc", nulls: "last" } }],
      take: 15,
      include: {
        societe: true,
        client: { select: { nom: true } },
        charge: { select: { nom: true, couleur: true } },
        taches: { select: { section: true, statut: true, echeance: true, libelle: true } },
      },
    }),
    db.dossier.findMany({
      where: {
        ...actifs,
        statutCommercial: "CONFIRME",
        dateDebut: { lte: dansSeptJours },
        OR: [{ dateFin: { gte: aujourdHui } }, { dateFin: null, dateDebut: { gte: aujourdHui } }],
      },
      orderBy: { dateDebut: "asc" },
      include: { societe: true, client: { select: { nom: true } }, charge: { select: { nom: true } } },
    }),
  ]);

  // Retards regroupés par personne assignée, sinon par rôle responsable (PLANIF, STOCK…).
  const parResponsable = new Map<string, { nom: string; couleur?: string; nb: number }>();
  for (const t of retards) {
    // « PLANIFICATION / STOCK » : le premier rôle cité est le responsable principal.
    const nom = t.assigne?.nom ?? t.responsable.split(" / ")[0];
    const ligne = parResponsable.get(nom) ?? { nom, couleur: t.assigne?.couleur, nb: 0 };
    ligne.nb++;
    parResponsable.set(nom, ligne);
  }

  const compte = (s: StatutCommercial) => historique.find((h) => h.statutCommercial === s)?._count ?? 0;
  const gagnes = compte("CONFIRME");
  const perdus = COMMERCIAUX_PERDUS.reduce((n, s) => n + compte(s), 0);

  const enCours = dossiersEnCours.map((d) => {
    const prochaine = d.taches
      .filter((t) => t.statut === "A_FAIRE" && t.echeance && t.echeance >= aujourdHui)
      .sort((a, b) => a.echeance!.getTime() - b.echeance!.getTime())[0];
    const retard = d.taches.filter((t) => t.statut === "A_FAIRE" && t.echeance && t.echeance < aujourdHui).length;
    return { ...d, sections: etatsSections(d.taches, aujourdHui), prochaine, retard, taches: undefined };
  });

  return {
    aujourdHui,
    nbActifs,
    nbRetards: retards.length,
    echeancesDuJour,
    confirmesAVenir: { nb: confirmesAVenir._count, montant: Number(confirmesAVenir._sum.montantEstime ?? 0) },
    pipeline: COMMERCIAUX_PIPELINE.map((s) => {
      const g = pipeline.find((p) => p.statutCommercial === s);
      return { statut: s, nb: g?._count ?? 0, montant: Number(g?._sum.montantEstime ?? 0) };
    }),
    transformation: { gagnes, perdus, taux: gagnes + perdus ? gagnes / (gagnes + perdus) : 0 },
    finances: Object.fromEntries(finances.map((f) => [f.statutFinancier, { nb: f._count, montant: Number(f._sum.montantEstime ?? 0) }])),
    devisSansReponse,
    retardsParResponsable: regrouperAutres([...parResponsable.values()].sort((a, b) => b.nb - a.nb), 8),
    enCours,
    semaine,
  };
}

/** Garde les n premiers et cumule le reste dans « Autres ». */
function regrouperAutres<T extends { nom: string; nb: number; couleur?: string }>(lignes: T[], n: number) {
  if (lignes.length <= n) return lignes;
  const autres = lignes.slice(n - 1).reduce((s, l) => s + l.nb, 0);
  return [...lignes.slice(0, n - 1), { nom: "Autres", nb: autres } as T];
}
