/**
 * Initialise la base : sociétés, comptes, modèle de procédure (146 étapes de l'Excel),
 * reprise des 482 dossiers du registre Excel et quelques dossiers de démonstration datés.
 *
 *   npm run db:seed            → tout
 *   SANS_DEMO=1 npm run db:seed → sans les dossiers de démonstration
 */
import { PrismaClient, type Ancre, type Prisma, type StatutCommercial, type StatutFinancier, type StatutOperationnel, type TypeDossier } from "@prisma/client";
import bcrypt from "bcryptjs";
import procedure from "./data/procedure.json";
import registre from "./data/dossiers.json";
import { ajouterJours, debutJour, genererTaches } from "../src/lib/procedure";

const db = new PrismaClient();
const MOT_DE_PASSE_DEMO = "demo2026";

type Statuts = {
  statutCommercial: StatutCommercial;
  statutOperationnel?: StatutOperationnel;
  statutFinancier?: StatutFinancier;
  type?: TypeDossier;
};

/** Le statut unique de l'Excel est réparti en trois statuts (commercial, opérationnel, financier). */
const CORRESPONDANCE: Record<string, Statuts> = {
  "PERDU": { statutCommercial: "PERDU" },
  "PAYE": { statutCommercial: "CONFIRME", statutOperationnel: "CLOTURE", statutFinancier: "PAYE" },
  "ABONDONNE PAR L'AGENCE": { statutCommercial: "ABANDONNE_AGENCE" },
  "DOSSIER REINTEGRE": { statutCommercial: "A_TRAITER" },
  "ANNULE PAR LE CLIENT": { statutCommercial: "ANNULE_CLIENT" },
  "A FACTURER EN PROFORMA": { statutCommercial: "CONFIRME", statutFinancier: "A_FACTURER_PROFORMA" },
  "IMPAYE": { statutCommercial: "CONFIRME", statutOperationnel: "PRE_CLOTURE", statutFinancier: "IMPAYE" },
  "GRATUIT": { statutCommercial: "CONFIRME", type: "GRATUIT" },
  "A FACTURER EN FINALE": { statutCommercial: "CONFIRME", statutOperationnel: "PRE_CLOTURE", statutFinancier: "A_FACTURER_FINALE" },
  "CONFIRME": { statutCommercial: "CONFIRME" },
  "DOSSIER INTERNE": { statutCommercial: "CONFIRME", type: "INTERNE" },
  "A TRAITER": { statutCommercial: "A_TRAITER" },
  "CLOTURE": { statutCommercial: "CONFIRME", statutOperationnel: "CLOTURE" },
  "A REGULARISER": { statutCommercial: "CONFIRME", statutFinancier: "A_REGULARISER" },
  "EN EXPOITATION": { statutCommercial: "CONFIRME", statutOperationnel: "EN_EXPLOITATION" },
  "ENVOYE EN INSTANCE": { statutCommercial: "EN_INSTANCE" },
  "LEAD CHAUD": { statutCommercial: "LEAD_CHAUD" },
  "LEAD FROID": { statutCommercial: "LEAD_FROID" },
  "CONTRAT CADRE": { statutCommercial: "CONFIRME", type: "CONTRAT_CADRE" },
  "EN BONNE VOIE": { statutCommercial: "EN_BONNE_VOIE" },
  "EN PREPARATION": { statutCommercial: "CONFIRME", statutOperationnel: "EN_PREPARATION" },
  "PRECLOTURE": { statutCommercial: "CONFIRME", statutOperationnel: "PRE_CLOTURE" },
  "CONTART ECHU": { statutCommercial: "CONFIRME", statutOperationnel: "CLOTURE", type: "CONTRAT_CADRE" },
  "REPORTE": { statutCommercial: "REPORTE" },
  "APPEL D'OFFRE": { statutCommercial: "A_TRAITER", type: "APPEL_OFFRES" },
};

function cleClient(nom: string) {
  return nom.normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^A-Z0-9]/gi, "").toUpperCase();
}

async function main() {
  const motDePasse = await bcrypt.hash(MOT_DE_PASSE_DEMO, 10);

  const mc = await db.societe.upsert({
    where: { code: "MC" },
    update: {},
    create: { code: "MC", nom: "MC", couleur: "#2a78d6" },
  });
  const endirect = await db.societe.upsert({
    where: { code: "ENDIRECT" },
    update: {},
    create: { code: "ENDIRECT", nom: "EN DIRECT", couleur: "#eb6834" },
  });
  const societes: Record<string, string> = { MC: mc.id, ENDIRECT: endirect.id };

  // Comptes de démonstration — à remplacer par les vrais utilisateurs (questionnaire 1.6).
  const comptes = [
    { nom: "Direction", email: "direction@erp.local", role: "DIRECTEUR", couleur: "#0b0b0b" },
    { nom: "Administrateur", email: "admin@erp.local", role: "ADMIN", couleur: "#52514e" },
    { nom: "Zineb", email: "zineb@erp.local", role: "CHARGE_COMPTE", couleur: "#e87ba4" },
    { nom: "Khaoula", email: "khaoula@erp.local", role: "CHARGE_COMPTE", couleur: "#2a78d6" },
    { nom: "Marouane", email: "marouane@erp.local", role: "CHARGE_COMPTE", couleur: "#eda100" },
    { nom: "Zahwa", email: "zahwa@erp.local", role: "CHARGE_COMPTE", couleur: "#1baf7a" },
    { nom: "Planification", email: "planif@erp.local", role: "PLANIFICATION", couleur: "#6250d6" },
    { nom: "Stock", email: "stock@erp.local", role: "STOCK", couleur: "#008300" },
    { nom: "Technicien terrain", email: "terrain@erp.local", role: "TERRAIN", couleur: "#e34948" },
  ] as const;
  const utilisateurs: Record<string, string> = {};
  for (const c of comptes) {
    const u = await db.utilisateur.upsert({
      where: { email: c.email },
      update: {},
      create: { ...c, motDePasse },
    });
    utilisateurs[c.nom] = u.id;
  }

  // Modèle de procédure issu de l'onglet TRACKER.
  let modele = await db.modeleProcedure.findFirst({ where: { actif: true } });
  if (!modele) {
    modele = await db.modeleProcedure.create({ data: { nom: "Procédure événement (Excel TRACKER)" } });
    await db.etapeModele.createMany({
      data: procedure.map((e) => ({
        modeleId: modele!.id,
        numero: e.numero as number,
        section: e.section,
        sectionNom: e.sectionNom,
        libelle: e.libelle,
        ancre: e.ancre as Ancre | null,
        decalage: e.decalage,
        ancreRepli: e.ancreRepli as Ancre | null,
        decalageRepli: e.decalageRepli,
        echeanceBrute: e.echeanceBrute || null,
        responsable: e.responsable || "—",
      })),
    });
  }

  // Reprise du registre Excel (clients dédoublonnés sur leur nom normalisé).
  if ((await db.dossier.count()) === 0) {
    const clients = new Map<string, string>();
    for (const d of registre) {
      const nom = d.client || "CLIENT DIVERS";
      const cle = cleClient(nom);
      if (!clients.has(cle)) {
        const c = await db.contact.create({ data: { nom, type: "CLIENT" } });
        clients.set(cle, c.id);
      }
    }
    for (const d of registre) {
      const statuts = CORRESPONDANCE[d.statutExcel.toUpperCase()] ?? { statutCommercial: "A_TRAITER" };
      // Les dossiers repris n'ont pas de dates : leur procédure s'ouvre depuis la fiche, à la demande.
      await db.dossier.create({
        data: {
          numero: d.numero,
          societeId: societes[d.societe] ?? mc.id,
          clientId: clients.get(cleClient(d.client || "CLIENT DIVERS")),
          intitule: d.intitule && d.intitule !== "TBD" ? d.intitule : "(intitulé à définir)",
          statutOrigine: d.statutExcel || null,
          ...statuts,
        },
      });
    }
    console.log(`Registre : ${registre.length} dossiers, ${clients.size} clients.`);
  }

  if (!process.env.SANS_DEMO && (await db.dossier.count({ where: { numero: { gte: 9001 } } })) === 0) {
    await creerDemo(societes, utilisateurs);
  }

  console.log(`Comptes : ${comptes.map((c) => c.email).join(", ")} — mot de passe « ${MOT_DE_PASSE_DEMO} ».`);
}

/**
 * Dossiers fictifs, datés autour d'aujourd'hui, pour montrer le tableau de bord en situation.
 * Numérotés à partir de 9001 et préfixés « [DÉMO] » : faciles à repérer et à supprimer.
 */
async function creerDemo(societes: Record<string, string>, utilisateurs: Record<string, string>) {
  const j = (n: number) => ajouterJours(debutJour(), n);
  const lieu = await db.contact.create({ data: { nom: "[DÉMO] Palais des congrès", type: "LIEU", ville: "Rabat" } });
  const client = await db.contact.create({
    data: { nom: "[DÉMO] Client exemple SA", type: "CLIENT", ville: "Casablanca", ice: "000000000000000" },
  });

  type Demo = Omit<Prisma.DossierUncheckedCreateInput, "numero" | "intitule" | "societeId"> & {
    intitule: string;
    societe: string;
    /** Proportion des tâches échues déjà faites (le reste est en retard). */
    tenue: number;
  };
  const demos: Demo[] = [
    { intitule: "Conférence annuelle", societe: "MC", chargeId: utilisateurs.Zineb, statutCommercial: "CONFIRME", statutOperationnel: "EN_PREPARATION", dateReception: j(-20), dateReperage: j(-15), dateConfirmation: j(-10), dateDebut: j(3), dateFin: j(4), montantEstime: 480000, tenue: 0.85 },
    { intitule: "Tournoi international — village partenaires", societe: "ENDIRECT", chargeId: utilisateurs.Zahwa, statutCommercial: "CONFIRME", statutOperationnel: "EN_PREPARATION", dateReception: j(-30), dateReperage: j(-24), dateConfirmation: j(-18), dateDebut: j(6), dateFin: j(12), montantEstime: 1250000, tenue: 0.6 },
    { intitule: "Cérémonie de lancement", societe: "MC", chargeId: utilisateurs.Khaoula, statutCommercial: "CONFIRME", statutOperationnel: "EN_EXPLOITATION", dateReception: j(-25), dateReperage: j(-20), dateConfirmation: j(-14), dateDebut: j(0), dateFin: j(1), montantEstime: 350000, tenue: 0.95 },
    { intitule: "Forum régional de l'investissement", societe: "MC", chargeId: utilisateurs.Marouane, statutCommercial: "CONFIRME", statutOperationnel: "PRE_CLOTURE", statutFinancier: "A_FACTURER_FINALE", dateReception: j(-45), dateReperage: j(-40), dateConfirmation: j(-33), dateDebut: j(-6), dateFin: j(-4), montantEstime: 620000, tenue: 0.7 },
    { intitule: "Séminaire direction", societe: "MC", chargeId: utilisateurs.Zineb, statutCommercial: "DEVIS_ENVOYE", dateReception: j(-9), dateReperage: j(-6), dateDebut: j(21), dateFin: j(22), montantEstime: 210000, tenue: 0.5 },
    { intitule: "Gala de fin d'année", societe: "MC", chargeId: utilisateurs.Khaoula, statutCommercial: "A_TRAITER", dateReception: j(-2), dateDebut: j(60), dateFin: j(60), montantEstime: 900000, tenue: 0.3 },
    { intitule: "Salon professionnel — stand", societe: "ENDIRECT", chargeId: utilisateurs.Zahwa, statutCommercial: "EN_BONNE_VOIE", dateReception: j(-12), dateReperage: j(-8), dateDebut: j(30), dateFin: j(33), montantEstime: 300000, tenue: 0.8 },
    { intitule: "Visite officielle", societe: "MC", chargeId: utilisateurs.Marouane, statutCommercial: "CONFIRME", statutOperationnel: "CLOTURE", statutFinancier: "IMPAYE", dateReception: j(-90), dateConfirmation: j(-80), dateDebut: j(-70), dateFin: j(-70), montantEstime: 150000, tenue: 1 },
  ];

  let numero = 9001;
  for (const { tenue, societe, intitule, ...d } of demos) {
    const dossier = await db.dossier.create({
      data: { ...d, numero: numero++, intitule: `[DÉMO] ${intitule}`, societeId: societes[societe], clientId: client.id, lieu: lieu.nom },
    });
    await db.$transaction((tx) => genererTaches(tx, dossier.id));
    // Les tâches échues sont faites selon la « tenue » du dossier ; les autres restent en retard.
    const echues = await db.tacheDossier.findMany({
      where: { dossierId: dossier.id, echeance: { lt: debutJour() } },
      orderBy: { echeance: "asc" },
    });
    const nbFaites = Math.round(echues.length * tenue);
    for (const [i, t] of echues.entries()) {
      if (i < nbFaites) {
        await db.tacheDossier.update({
          where: { id: t.id },
          data: { statut: i % 9 === 4 ? "NON_APPLICABLE" : "FAIT", faitLe: t.echeance, faitParId: d.chargeId ?? null },
        });
      }
    }
  }
  console.log(`Démo : ${demos.length} dossiers fictifs (n° 9001 et suivants).`);
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(() => db.$disconnect());
