// src/controllers/videoController.js
const fs = require('fs');
const rangeParser = require('range-parser');
const Video = require('../models/Video');
const Measurement = require('../models/Measurement');
const StorageService = require('../services/storageService');
const VideoService = require('../services/videoService');
const { sendError, asyncHandler } = require('../utils/errorHandler');
const config = require('../config');
const path = require('path');

/**
 * Contrôleur pour les opérations liées aux vidéos
 */
class VideoController {
  /**
   * Récupère toutes les vidéos
   */
  static getAllVideos = asyncHandler(async (req, res) => {
    const videos = await Video.getAllVideos();
    res.json(videos);
  });

  /**
   * Récupère une vidéo par son ID
   */
  static getVideoById = asyncHandler(async (req, res) => {
    const videoId = req.params.id;
    
    if (!videoId || isNaN(videoId)) {
      return sendError('ID de vidéo invalide', res, null, 400);
    }
    
    const video = await Video.getVideoById(videoId);
    
    if (!video) {
      return sendError('Vidéo non trouvée', res, null, 404);
    }
    
    res.json(video);
  });

  /**
   * Crée une nouvelle vidéo
   */
  static createVideo = asyncHandler(async (req, res) => {
    const { project_id, measurement_ids, name, resolution, duration } = req.body;
    
    if (!project_id || !measurement_ids || !name || !resolution || !duration) {
      return sendError('Tous les champs sont requis', res, null, 400);
    }
    
    console.log('[VIDEO] Création de vidéo avec les mesures:', measurement_ids);
    
    try {
      // Crée l'entrée vidéo dans la base de données
      const video = await Video.createVideo(
        project_id, 
        measurement_ids, 
        name, 
        resolution, 
        duration, 
        config.status.waiting
      );
      
      if (!video || !video.id) {
        return sendError('Erreur lors de la création de la vidéo', res, null, 500);
      }
      
      console.log('[VIDEO] Vidéo créée avec succès:', video.id);
      
      // Parse et récupère les chemins des images
      const pathList = await this.getMeasurementPathList(measurement_ids, project_id);
      
      if (!pathList || pathList.length === 0) {
        return sendError('Aucun chemin trouvé pour les mesures', res, null, 404);
      }
      
      // Parse la résolution (ex: 1920x1080)
      const [resWidth, resHeight] = resolution.split('x').map(Number);
      if (isNaN(resWidth) || isNaN(resHeight)) {
        return sendError('Format de résolution invalide. Utiliser LARGEURxHAUTEUR (ex: 1920x1080)', res, null, 400);
      }
      
      // Démarre le processus de rendu en arrière-plan
      VideoService.createVideoFromImages(
        project_id, 
        pathList, 
        duration, 
        video.id, 
        resWidth, 
        resHeight
      )
        .then(videoFile => {
          console.log('[VIDEO] Rendu terminé:', videoFile);
          return Video.updateVideoFilePath(video.id, videoFile);
        })
        .catch(error => {
          console.error('[VIDEO] Échec du rendu:', error);
        });
      
      // Réponse immédiate
      res.json({
        message: 'Vidéo créée avec succès et le rendu a démarré',
        id: video.id
      });
    } catch (err) {
      console.error('[VIDEO] Erreur lors de la création de la vidéo:', err);
      sendError('Erreur lors de la création de la vidéo', res, err, 500);
    }
  });

  /**
   * Supprime une vidéo existante
   */
  static deleteVideo = asyncHandler(async (req, res) => {
    const videoId = req.params.id;
    
    if (!videoId || isNaN(videoId)) {
      return sendError('ID de vidéo invalide', res, null, 400);
    }
    
    // Récupère les informations de la vidéo
    const video = await Video.getVideoById(videoId);
    
    if (!video) {
      return sendError('Vidéo non trouvée', res, null, 404);
    }
    
    // Supprime le fichier vidéo s'il existe
    if (video.video_file) {
      try {
        console.log('[VIDEO] Suppression du fichier vidéo:', video.video_file);
        await StorageService.deleteFile(video.video_file);
      } catch (err) {
        console.error('[VIDEO] Erreur lors de la suppression du fichier vidéo:', err);
      }
    } else {
      console.log('[VIDEO] Pas de fichier vidéo à supprimer');
    }
    
    // Supprime l'entrée dans la base de données
    await Video.deleteVideo(videoId);
    
    res.json({ message: 'Vidéo supprimée avec succès' });
  });

  /**
   * Récupère le fichier vidéo avec support du streaming
   */
  static getVideoFile = asyncHandler(async (req, res) => {
    const videoId = req.params.video_id;
    const video = await Video.getVideoById(videoId);
    
    if (!video) {
      return sendError('Vidéo non trouvée', res, null, 404);
    }
    
    if (video.status === config.status.waiting || video.status === config.status.inProgress) {
      return sendError('Vidéo pas encore produite', res, null, 400);
    }
    
    let videoPath = video.video_file;
    
    // Vérifie si le fichier existe
    try {
      await fs.promises.access(videoPath, fs.constants.F_OK);
    } catch (err) {
      console.error('[VIDEO] Fichier vidéo non trouvé:', err);
      return sendError('Fichier vidéo non trouvé', res, err, 404);
    }
    
    const stat = fs.statSync(videoPath);
    const fileSize = stat.size;
    const range = req.headers.range;
    
    if (range) {
      const parts = rangeParser(fileSize, range);
      const start = parts[0].start;
      const end = parts[0].end;
      const chunksize = (end - start) + 1;
      const file = fs.createReadStream(videoPath, { start, end });
      
      const head = {
        'Content-Range': `bytes ${start}-${end}/${fileSize}`,
        'Accept-Ranges': 'bytes',
        'Content-Length': chunksize,
        'Content-Type': 'video/mp4',
      };
      
      res.writeHead(206, head);
      file.pipe(res);
    } else {
      const head = {
        'Content-Length': fileSize,
        'Content-Type': 'video/mp4',
      };
      
      res.writeHead(200, head);
      fs.createReadStream(videoPath).pipe(res);
    }
  });

  /**
   * Récupère la progression d'une vidéo en cours de rendu
   */
  static getVideoProgress = asyncHandler(async (req, res) => {
    const videoId = req.params.video_id;
    
    try {
      const progress = await VideoService.getVideoProgress(videoId);
      
      res.json({
        progress: progress.progress,
        elapsed: progress.elapsed,
        eta: progress.eta,
        status: VideoService.getStatusLabel(progress.status)
      });
    } catch (error) {
      sendError('Erreur de récupération de la progression', res, error, 500);
    }
  });

  /**
   * Récupère les chemins d'images à partir d'une liste d'IDs de mesures
   * Méthode utilitaire privée
   */
  static async getMeasurementPathList(idListJson, projectId) {
    let idList;
    
    try {
      idList = JSON.parse(idListJson);
      
      if (!Array.isArray(idList)) {
        throw new Error('Le format de la liste des IDs est invalide');
      }
    } catch (e) {
      console.error('[VIDEO] Erreur de parsing de la liste d\'IDs:', e);
      return [];
    }
    
    const pathList = [];
    
    for (const orderId of idList) {
      const measurement = await Measurement.getMeasurementByProjectAndOrderId(projectId, orderId);
      
      if (measurement && measurement.path) {
        pathList.push(measurement.path);
      }
    }
    
    return pathList;
  }
}

module.exports = VideoController;