import 'dotenv/config';
import { PrismaClient } from '@prisma/client';
import { PrismaMssql } from '@prisma/adapter-mssql';

// Fixtures de DEV/TEST. À lancer après `npm run seed` (dépend des données de référence).
// Non destructif : si déjà chargées (email témoin présent), le script s'arrête.

function getEnv(name: string): string {
  const value = process.env[name];

  if (!value) {
    throw new Error(`Variable d'environnement manquante: ${name}`);
  }

  return value;
}

function required<T>(value: T | null, message: string): T {
  if (value === null) {
    throw new Error(message);
  }

  return value;
}

const adapter = new PrismaMssql({
  server: getEnv('DB_SERVER'),
  port: Number(process.env.DB_PORT ?? 1433),
  database: getEnv('DB_NAME'),
  user: getEnv('DB_USER'),
  password: getEnv('DB_PASSWORD'),
  options: {
    encrypt: true,
    trustServerCertificate: true,
  },
});

const prisma = new PrismaClient({ adapter });

const WITNESS_EMAIL = 'marie.dupont@ensitech.eu';

const now = new Date();
function daysAgo(n: number): Date {
  const d = new Date(now);
  d.setDate(d.getDate() - n);
  return d;
}
function daysFromNow(n: number): Date {
  const d = new Date(now);
  d.setDate(d.getDate() + n);
  return d;
}

async function main(): Promise<void> {
  // Idempotence : on ne recharge pas si déjà présent.
  const witness = await prisma.utilisateur.findUnique({ where: { email: WITNESS_EMAIL } });
  if (witness) {
    console.log('[fixtures] deja chargees — rien a faire');
    return;
  }

  // Références (doivent exister via le seed).
  const roleEtudiant = required(
    await prisma.role.findUnique({ where: { code: 'ETUDIANT' } }),
    "Role ETUDIANT manquant — lance d'abord `npm run seed`",
  );
  const roleResponsable = required(
    await prisma.role.findUnique({ where: { code: 'RESPONSABLE' } }),
    "Role RESPONSABLE manquant — lance d'abord `npm run seed`",
  );
  const campus = required(
    await prisma.campus.findFirst({ where: { nom: 'Campus Saint-Christophe' } }),
    "Campus manquant — lance d'abord `npm run seed`",
  );
  const salle = required(
    await prisma.sallePret.findFirst({ where: { campusId: campus.id } }),
    "Salle de pret manquante — lance d'abord `npm run seed`",
  );
  const poste = required(
    await prisma.posteEmprunt.findFirst({ where: { sallePretId: salle.id } }),
    "Poste manquant — lance d'abord `npm run seed`",
  );
  const catOrdi = required(
    await prisma.categorieMateriel.findFirst({ where: { nom: 'Ordinateur portable' } }),
    'Categorie Ordinateur portable manquante',
  );
  const catVideo = required(
    await prisma.categorieMateriel.findFirst({ where: { nom: 'Vidéoprojecteur' } }),
    'Categorie Vidéoprojecteur manquante',
  );
  const catTablette = required(
    await prisma.categorieMateriel.findFirst({ where: { nom: 'Tablette' } }),
    'Categorie Tablette manquante',
  );
  const catCamera = required(
    await prisma.categorieMateriel.findFirst({ where: { nom: 'Caméra' } }),
    'Categorie Caméra manquante',
  );
  const catAudio = required(
    await prisma.categorieMateriel.findFirst({ where: { nom: 'Périphérique audio' } }),
    'Categorie Périphérique audio manquante',
  );

  // Utilisateurs : 3 étudiants (2 info @ensitech.eu, 1 autre @ensup.eu) + 1 responsable.
  const marie = await prisma.utilisateur.create({
    data: {
      microsoftId: 'ms-marie-dupont',
      nom: 'Dupont',
      prenom: 'Marie',
      email: WITNESS_EMAIL,
      classe: 'B3 Informatique',
      roleId: roleEtudiant.id,
      campusId: campus.id,
    },
  });
  const lucas = await prisma.utilisateur.create({
    data: {
      microsoftId: 'ms-lucas-martin',
      nom: 'Martin',
      prenom: 'Lucas',
      email: 'lucas.martin@ensitech.eu',
      classe: 'B2 Informatique',
      roleId: roleEtudiant.id,
      campusId: campus.id,
    },
  });
  const sofia = await prisma.utilisateur.create({
    data: {
      microsoftId: 'ms-sofia-bernard',
      nom: 'Bernard',
      prenom: 'Sofia',
      email: 'sofia.bernard@ensup.eu',
      classe: 'B1 Marketing',
      roleId: roleEtudiant.id,
      campusId: campus.id,
    },
  });
  const responsable = await prisma.utilisateur.create({
    data: {
      microsoftId: 'ms-karim-benali',
      nom: 'Benali',
      prenom: 'Karim',
      email: 'karim.benali@ensup.eu',
      classe: null, // un responsable n'a pas de classe
      roleId: roleResponsable.id,
      campusId: campus.id,
    },
  });
  console.log('[fixtures] 4 utilisateurs crees (3 etudiants + 1 responsable)');

  // Cartes étudiantes (qrCode unique par carte).
  await prisma.carteEtudiante.createMany({
    data: [
      {
        utilisateurId: marie.id,
        qrCode: 'QR-ETU-0001',
        dateActivation: daysAgo(300),
        dateExpiration: daysFromNow(400),
      },
      {
        utilisateurId: lucas.id,
        qrCode: 'QR-ETU-0002',
        dateActivation: daysAgo(300),
        dateExpiration: daysFromNow(400),
      },
      {
        utilisateurId: sofia.id,
        qrCode: 'QR-ETU-0003',
        dateActivation: daysAgo(300),
        dateExpiration: daysFromNow(400),
      },
    ],
  });
  console.log('[fixtures] 3 cartes etudiantes creees');

  // Accessoires.
  const chargeur = await prisma.accessoire.create({
    data: { nom: 'Chargeur', description: 'Chargeur secteur.' },
  });
  const souris = await prisma.accessoire.create({
    data: { nom: 'Souris', description: 'Souris filaire.' },
  });
  const housse = await prisma.accessoire.create({
    data: { nom: 'Housse', description: 'Housse de protection.' },
  });

  // Matériels (statut explicite — pas de défaut sur ce champ).
  const pcDell = await prisma.materiel.create({
    data: {
      nom: 'PC portable Dell Latitude',
      marque: 'Dell',
      modele: 'Latitude 5440',
      reference: 'REF-PC-001',
      numeroInventaire: 'INV-PC-001',
      numeroSerie: 'SN-DELL-001',
      statut: 'EMPRUNTE',
      etatGeneral: 'Bon etat',
      categorieId: catOrdi.id,
      campusId: campus.id,
    },
  });
  const projecteur = await prisma.materiel.create({
    data: {
      nom: 'Vidéoprojecteur Epson',
      marque: 'Epson',
      modele: 'EB-W06',
      reference: 'REF-VP-001',
      numeroInventaire: 'INV-VP-001',
      numeroSerie: 'SN-EPSON-001',
      statut: 'EMPRUNTE',
      etatGeneral: 'Bon etat',
      categorieId: catVideo.id,
      campusId: campus.id,
    },
  });
  const ipad = await prisma.materiel.create({
    data: {
      nom: 'Tablette iPad',
      marque: 'Apple',
      modele: 'iPad 10',
      reference: 'REF-TAB-001',
      numeroInventaire: 'INV-TAB-001',
      numeroSerie: 'SN-IPAD-001',
      statut: 'DISPONIBLE',
      etatGeneral: 'Bon etat',
      categorieId: catTablette.id,
      campusId: campus.id,
    },
  });
  const camera = await prisma.materiel.create({
    data: {
      nom: 'Caméra Sony',
      marque: 'Sony',
      modele: 'Alpha 6400',
      reference: 'REF-CAM-001',
      numeroInventaire: 'INV-CAM-001',
      numeroSerie: 'SN-SONY-001',
      statut: 'DETERIORE',
      etatGeneral: 'Housse manquante au retour',
      categorieId: catCamera.id,
      campusId: campus.id,
    },
  });
  await prisma.materiel.create({
    data: {
      nom: 'Micro Rode',
      marque: 'Rode',
      modele: 'NT-USB',
      reference: 'REF-MIC-001',
      numeroInventaire: 'INV-MIC-001',
      numeroSerie: 'SN-RODE-001',
      statut: 'DISPONIBLE',
      etatGeneral: 'Bon etat',
      categorieId: catAudio.id,
      campusId: campus.id,
    },
  });
  await prisma.materiel.create({
    data: {
      nom: 'PC portable HP',
      marque: 'HP',
      modele: 'ProBook 450',
      reference: 'REF-PC-002',
      numeroInventaire: 'INV-PC-002',
      numeroSerie: 'SN-HP-002',
      statut: 'MAINTENANCE',
      etatGeneral: 'En revision',
      categorieId: catOrdi.id,
      campusId: campus.id,
    },
  });
  console.log('[fixtures] 6 materiels crees');

  // Accessoires attendus (obligatoire explicite — pas de défaut).
  await prisma.materielAccessoire.createMany({
    data: [
      { materielId: pcDell.id, accessoireId: chargeur.id, quantiteAttendue: 1, obligatoire: true },
      { materielId: pcDell.id, accessoireId: souris.id, quantiteAttendue: 1, obligatoire: false },
      { materielId: camera.id, accessoireId: housse.id, quantiteAttendue: 1, obligatoire: true },
      { materielId: camera.id, accessoireId: chargeur.id, quantiteAttendue: 1, obligatoire: true },
    ],
  });

  // Emprunt 1 — EN_COURS (Marie / PC Dell) + checklist de départ.
  const empruntEnCours = await prisma.emprunt.create({
    data: {
      utilisateurId: marie.id,
      materielId: pcDell.id,
      campusId: campus.id,
      sallePretId: salle.id,
      posteEmpruntId: poste.id,
      dateEmprunt: daysAgo(2),
      dateRetourPrevue: daysFromNow(5),
      statut: 'EN_COURS',
      modeIdentificationEmprunt: 'QR_CODE',
    },
  });
  const clDepart1 = await prisma.checklist.create({
    data: {
      empruntId: empruntEnCours.id,
      utilisateurId: marie.id,
      type: 'DEPART',
      dateVerification: daysAgo(2),
    },
  });
  await prisma.checklistElement.createMany({
    data: [
      {
        checklistId: clDepart1.id,
        accessoireId: chargeur.id,
        nomElement: 'Chargeur',
        etat: 'PRESENT',
        quantiteConstatee: 1,
      },
      {
        checklistId: clDepart1.id,
        accessoireId: souris.id,
        nomElement: 'Souris',
        etat: 'PRESENT',
        quantiteConstatee: 1,
      },
    ],
  });

  // Emprunt 2 — EN_RETARD (Lucas / Vidéoprojecteur), date de retour dépassée.
  const empruntEnRetard = await prisma.emprunt.create({
    data: {
      utilisateurId: lucas.id,
      materielId: projecteur.id,
      campusId: campus.id,
      sallePretId: salle.id,
      posteEmpruntId: poste.id,
      dateEmprunt: daysAgo(20),
      dateRetourPrevue: daysAgo(6),
      statut: 'EN_RETARD',
      modeIdentificationEmprunt: 'MAIL_ENSUP',
    },
  });
  await prisma.checklist.create({
    data: {
      empruntId: empruntEnRetard.id,
      utilisateurId: lucas.id,
      type: 'DEPART',
      dateVerification: daysAgo(20),
    },
  });

  // Emprunt 3 — CLOTURE (Marie / iPad), retour conforme.
  const empruntCloture = await prisma.emprunt.create({
    data: {
      utilisateurId: marie.id,
      materielId: ipad.id,
      campusId: campus.id,
      sallePretId: salle.id,
      posteEmpruntId: poste.id,
      dateEmprunt: daysAgo(10),
      dateRetourPrevue: daysAgo(3),
      dateRetourReelle: daysAgo(3),
      statut: 'CLOTURE',
      modeIdentificationEmprunt: 'QR_CODE',
      modeIdentificationRetour: 'QR_CODE',
    },
  });
  await prisma.checklist.create({
    data: {
      empruntId: empruntCloture.id,
      utilisateurId: marie.id,
      type: 'DEPART',
      dateVerification: daysAgo(10),
    },
  });
  await prisma.checklist.create({
    data: {
      empruntId: empruntCloture.id,
      utilisateurId: marie.id,
      type: 'RETOUR',
      dateVerification: daysAgo(3),
    },
  });

  // Emprunt 4 — RETOUR_NON_CONFORME (Sofia / Caméra) : housse absente au retour.
  const empruntNonConforme = await prisma.emprunt.create({
    data: {
      utilisateurId: sofia.id,
      materielId: camera.id,
      campusId: campus.id,
      sallePretId: salle.id,
      posteEmpruntId: poste.id,
      dateEmprunt: daysAgo(5),
      dateRetourPrevue: daysAgo(1),
      dateRetourReelle: daysAgo(1),
      statut: 'RETOUR_NON_CONFORME',
      modeIdentificationEmprunt: 'MAIL_ENSUP',
      modeIdentificationRetour: 'MAIL_ENSUP',
      commentaireRetour: 'Housse non rapportee.',
    },
  });
  const clDepart4 = await prisma.checklist.create({
    data: {
      empruntId: empruntNonConforme.id,
      utilisateurId: sofia.id,
      type: 'DEPART',
      dateVerification: daysAgo(5),
    },
  });
  await prisma.checklistElement.createMany({
    data: [
      {
        checklistId: clDepart4.id,
        accessoireId: housse.id,
        nomElement: 'Housse',
        etat: 'PRESENT',
        quantiteConstatee: 1,
      },
      {
        checklistId: clDepart4.id,
        accessoireId: chargeur.id,
        nomElement: 'Chargeur',
        etat: 'PRESENT',
        quantiteConstatee: 1,
      },
    ],
  });
  const clRetour4 = await prisma.checklist.create({
    data: {
      empruntId: empruntNonConforme.id,
      utilisateurId: sofia.id,
      type: 'RETOUR',
      dateVerification: daysAgo(1),
    },
  });
  await prisma.checklistElement.createMany({
    data: [
      {
        checklistId: clRetour4.id,
        accessoireId: housse.id,
        nomElement: 'Housse',
        etat: 'ABSENT',
        quantiteConstatee: 0,
        commentaire: 'Manquante au retour',
      },
      {
        checklistId: clRetour4.id,
        accessoireId: chargeur.id,
        nomElement: 'Chargeur',
        etat: 'PRESENT',
        quantiteConstatee: 1,
      },
    ],
  });
  console.log('[fixtures] 4 emprunts + checklists crees');

  // Anomalie sur l'emprunt non conforme + notification au responsable.
  const anomalie = await prisma.anomalie.create({
    data: {
      empruntId: empruntNonConforme.id,
      materielId: camera.id,
      utilisateurId: sofia.id,
      type: 'ACCESSOIRE_MANQUANT',
      description: 'Housse declaree presente au depart, absente au retour.',
      statut: 'DETECTEE',
      detecteeAutomatiquement: true,
      dateDetection: daysAgo(1),
    },
  });
  await prisma.notification.create({
    data: {
      utilisateurId: responsable.id,
      anomalieId: anomalie.id,
      titre: 'Nouvelle anomalie detectee',
      message: 'Retour non conforme sur la Caméra Sony (housse manquante).',
      type: 'ANOMALIE',
    },
  });
  console.log('[fixtures] 1 anomalie + 1 notification creees');

  // Historique : quelques traces.
  await prisma.historique.createMany({
    data: [
      {
        utilisateurId: marie.id,
        empruntId: empruntEnCours.id,
        materielId: pcDell.id,
        campusId: campus.id,
        sallePretId: salle.id,
        posteEmpruntId: poste.id,
        action: 'CREATION_EMPRUNT',
        description: 'Emprunt du PC portable Dell.',
        dateAction: daysAgo(2),
      },
      {
        utilisateurId: sofia.id,
        empruntId: empruntNonConforme.id,
        materielId: camera.id,
        campusId: campus.id,
        sallePretId: salle.id,
        posteEmpruntId: poste.id,
        action: 'CREATION_ANOMALIE',
        description: 'Anomalie detectee au retour de la Caméra Sony.',
        dateAction: daysAgo(1),
      },
    ],
  });
  console.log("[fixtures] 2 entrees d'historique creees");
}

main()
  .then(async () => {
    await prisma.$disconnect();
    console.log('[fixtures] termine');
  })
  .catch(async (error: unknown) => {
    console.error('[fixtures] echec', error);
    await prisma.$disconnect();
    process.exit(1);
  });
