import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { DocumentLegal } from '../../entities/document-legal.entity';
import { AcceptationDocument } from '../../entities/acceptation-document.entity';
import * as crypto from 'crypto';
import * as fs from 'fs/promises';
import * as path from 'path';

@Injectable()
export class DocumentsLegauxService {
  private readonly UPLOAD_DIR = '/app/documents/legaux';

  constructor(
    @InjectRepository(DocumentLegal)
    private docRepo: Repository<DocumentLegal>,
    @InjectRepository(AcceptationDocument)
    private acceptationRepo: Repository<AcceptationDocument>,
  ) {}

  /**
   * Récupérer les documents actifs (CGU + Privacy)
   */
  async getDocumentsActifs(): Promise<{ cgu: DocumentLegal; privacy: DocumentLegal }> {
    const cgu = await this.docRepo.findOne({
      where: { type: 'cgu', actif: true },
    });

    const privacy = await this.docRepo.findOne({
      where: { type: 'privacy', actif: true },
    });

    if (!cgu || !privacy) {
      throw new NotFoundException('Documents légaux manquants');
    }

    return { cgu, privacy };
  }

  /**
   * Uploader une nouvelle version d'un document
   */
  async uploadNouvelleVersion(
    type: 'cgu' | 'privacy',
    file: Express.Multer.File,
    userId?: string | null,
  ): Promise<DocumentLegal> {
    // Validation du type de fichier
    if (file.mimetype !== 'application/pdf') {
      throw new BadRequestException('Seuls les fichiers PDF sont acceptés');
    }

    // Validation de la taille (max 10MB)
    const maxSize = 10 * 1024 * 1024; // 10MB
    if (file.size > maxSize) {
      throw new BadRequestException('Le fichier ne doit pas dépasser 10MB');
    }

    // 1. Calculer la prochaine version
    const lastDoc = await this.docRepo.findOne({
      where: { type },
      order: { version: 'DESC' },
    });
    const nouvelleVersion = (lastDoc?.version || 0) + 1;

    // 2. Calculer le hash SHA-256 du fichier
    const fileBuffer = file.buffer;
    const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');

    // 3. Générer le nom de fichier unique
    const timestamp = Date.now();
    const fileName = `${type}_v${nouvelleVersion}_${timestamp}.pdf`;
    const filePath = path.join(this.UPLOAD_DIR, fileName);

    // 4. Créer le répertoire si nécessaire et sauvegarder le fichier
    await fs.mkdir(this.UPLOAD_DIR, { recursive: true });
    await fs.writeFile(filePath, fileBuffer);

    // 5. Créer l'entrée en BDD
    const document = this.docRepo.create({
      type,
      version: nouvelleVersion,
      fichier_nom: file.originalname,
      fichier_path: filePath,
      fichier_hash: hash,
      actif: false, // Pas actif par défaut
      televersePar: userId ? ({ id: userId } as any) : null,
      televerseLe: new Date(),
    });

    return await this.docRepo.save(document);
  }

  /**
   * Activer une version (désactive automatiquement l'ancienne)
   */
  async activerVersion(documentId: string): Promise<void> {
    const document = await this.docRepo.findOne({ where: { id: documentId } });

    if (!document) {
      throw new NotFoundException('Document non trouvé');
    }

    // Transaction : désactiver l'ancienne version, activer la nouvelle
    await this.docRepo.manager.transaction(async (manager) => {
      // Désactiver toutes les versions de ce type
      await manager.update(
        DocumentLegal,
        { type: document.type, actif: true },
        { actif: false },
      );

      // Activer la nouvelle version
      await manager.update(
        DocumentLegal,
        { id: documentId },
        { actif: true, activeLe: new Date() },
      );
    });
  }

  /**
   * Lister toutes les versions d'un type de document
   */
  async listerVersions(type: 'cgu' | 'privacy'): Promise<DocumentLegal[]> {
    return await this.docRepo.find({
      where: { type },
      order: { version: 'DESC' },
      relations: ['televersePar'],
    });
  }

  /**
   * Télécharger un document (retourne le buffer et le nom)
   */
  async telechargerDocument(documentId: string): Promise<{ stream: Buffer; filename: string }> {
    const document = await this.docRepo.findOne({ where: { id: documentId } });

    if (!document) {
      throw new NotFoundException('Document non trouvé');
    }

    try {
      const fileBuffer = await fs.readFile(document.fichier_path);

      return {
        stream: fileBuffer,
        filename: document.fichier_nom,
      };
    } catch (error) {
      throw new NotFoundException('Fichier introuvable sur le système de fichiers');
    }
  }

  /**
   * Vérifier l'intégrité d'un document (hash SHA-256)
   */
  async verifierIntegrite(documentId: string): Promise<boolean> {
    const document = await this.docRepo.findOne({ where: { id: documentId } });

    if (!document) {
      throw new NotFoundException('Document non trouvé');
    }

    try {
      const fileBuffer = await fs.readFile(document.fichier_path);
      const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');

      return hash === document.fichier_hash;
    } catch (error) {
      return false;
    }
  }

  /**
   * Enregistrer une acceptation de document (lors de l'inscription)
   */
  async enregistrerAcceptation(
    userId: string,
    documentId: string,
    typeDocument: 'cgu' | 'privacy',
    versionDocument: number,
    ipAddress: string | null,
    userAgent: string | null,
  ): Promise<AcceptationDocument> {
    const acceptation = this.acceptationRepo.create({
      utilisateur: { id: userId } as any,
      document: { id: documentId } as any,
      type_document: typeDocument,
      version_document: versionDocument,
      ip_address: ipAddress,
      user_agent: userAgent,
    });

    return await this.acceptationRepo.save(acceptation);
  }

  /**
   * Récupérer l'historique des acceptations d'un utilisateur
   */
  async getAcceptationsUtilisateur(userId: string): Promise<AcceptationDocument[]> {
    return await this.acceptationRepo.find({
      where: { utilisateur: { id: userId } },
      order: { accepteLe: 'DESC' },
      relations: ['document'],
    });
  }
}

