package handlers

import (
	"fmt"
	"net/http"
	"time"

	"veza-backend-api/internal/services"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

// UploadRequest requête pour upload de fichier
type UploadRequest struct {
	TrackID  uuid.UUID `form:"track_id" binding:"required"`
	FileType string    `form:"file_type" binding:"required,oneof=audio image video"`
	Title    string    `form:"title" binding:"required,min=1,max=255"`
	Artist   string    `form:"artist" binding:"required,min=1,max=255"`
	Duration int       `form:"duration" binding:"min=0"`
	Metadata string    `form:"metadata"`
}

// UploadResponse réponse pour upload
type UploadResponse struct {
	ID        uuid.UUID `json:"id"`
	TrackID   uuid.UUID `json:"track_id"`
	FileName  string    `json:"file_name"`
	FileSize  int64     `json:"file_size"`
	FileType  string    `json:"file_type"`
	Checksum  string    `json:"checksum"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
}

// UploadHandler gère les uploads de fichiers
type UploadHandler struct {
	uploadValidator *services.UploadValidator
	auditService    *services.AuditService
	logger          *zap.Logger
}

// NewUploadHandler crée un nouveau handler d'upload
func NewUploadHandler(
	uploadValidator *services.UploadValidator,
	auditService *services.AuditService,
	logger *zap.Logger,
) *UploadHandler {
	return &UploadHandler{
		uploadValidator: uploadValidator,
		auditService:    auditService,
		logger:          logger,
	}
}

// UploadFile gère l'upload d'un fichier
func (uh *UploadHandler) UploadFile() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
			return
		}

		// Parser la requête multipart
		var req UploadRequest
		if err := c.ShouldBind(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		// Récupérer le fichier
		fileHeader, err := c.FormFile("file")
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
			return
		}

		// Valider le fichier
		validationResult, err := uh.uploadValidator.ValidateFile(fileHeader, req.FileType)
		if err != nil {
			uh.logger.Error("File validation failed",
				zap.Error(err),
				zap.String("user_id", userID.String()),
				zap.String("file_name", fileHeader.Filename),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "File validation failed"})
			return
		}

		// Vérifier si le fichier est valide
		if !validationResult.Valid {
			uh.logger.Warn("Invalid file uploaded",
				zap.String("user_id", userID.String()),
				zap.String("file_name", fileHeader.Filename),
				zap.String("error", validationResult.Error),
			)
			c.JSON(http.StatusBadRequest, gin.H{"error": validationResult.Error})
			return
		}

		// Vérifier si le fichier a été mis en quarantaine
		if validationResult.Quarantined {
			uh.logger.Warn("File quarantined",
				zap.String("user_id", userID.String()),
				zap.String("file_name", fileHeader.Filename),
				zap.String("reason", validationResult.Error),
			)
			c.JSON(http.StatusBadRequest, gin.H{
				"error":   "File rejected for security reasons",
				"details": validationResult.Error,
			})
			return
		}

		// Créer l'enregistrement en base de données
		// Note: Dans un vrai environnement, il faudrait sauvegarder le fichier
		// et créer l'enregistrement dans la table tracks
		uploadID := uuid.New()

		// Log l'upload dans l'audit
		err = uh.auditService.LogUpload(
			c.Request.Context(),
			userID,
			req.TrackID,
			fileHeader.Filename,
			validationResult.FileSize,
			c.ClientIP(),
			c.GetHeader("User-Agent"),
		)
		if err != nil {
			uh.logger.Error("Failed to log upload audit",
				zap.Error(err),
				zap.String("user_id", userID.String()),
			)
			// Ne pas faire échouer l'upload pour une erreur d'audit
		}

		uh.logger.Info("File uploaded successfully",
			zap.String("user_id", userID.String()),
			zap.String("upload_id", uploadID.String()),
			zap.String("file_name", fileHeader.Filename),
			zap.Int64("file_size", validationResult.FileSize),
			zap.String("file_type", validationResult.FileType),
		)

		// Retourner la réponse
		response := &UploadResponse{
			ID:        uploadID,
			TrackID:   req.TrackID,
			FileName:  fileHeader.Filename,
			FileSize:  validationResult.FileSize,
			FileType:  validationResult.FileType,
			Checksum:  validationResult.Checksum,
			Status:    "uploaded",
			CreatedAt: time.Now(),
		}

		c.JSON(http.StatusCreated, gin.H{
			"message": "File uploaded successfully",
			"data":    response,
		})
	}
}

// GetUploadStatus récupère le statut d'un upload
func (uh *UploadHandler) GetUploadStatus() gin.HandlerFunc {
	return func(c *gin.Context) {
		uploadIDStr := c.Param("id")
		uploadID, err := uuid.Parse(uploadIDStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid upload ID"})
			return
		}

		// Récupérer le statut depuis la base de données
		// Note: Dans un vrai environnement, il faudrait interroger la DB
		c.JSON(http.StatusOK, gin.H{
			"id":       uploadID,
			"status":   "completed",
			"progress": 100,
		})
	}
}

// DeleteUpload supprime un upload
func (uh *UploadHandler) DeleteUpload() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
			return
		}

		uploadIDStr := c.Param("id")
		uploadID, err := uuid.Parse(uploadIDStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid upload ID"})
			return
		}

		// Log la suppression dans l'audit
		err = uh.auditService.LogDeletion(
			c.Request.Context(),
			userID,
			"upload",
			uploadID,
			c.ClientIP(),
			c.GetHeader("User-Agent"),
		)
		if err != nil {
			uh.logger.Error("Failed to log deletion audit",
				zap.Error(err),
				zap.String("user_id", userID.String()),
			)
		}

		uh.logger.Info("Upload deleted",
			zap.String("user_id", userID.String()),
			zap.String("upload_id", uploadID.String()),
		)

		c.JSON(http.StatusOK, gin.H{
			"message": "Upload deleted successfully",
		})
	}
}

// GetUploadStats récupère les statistiques d'upload
func (uh *UploadHandler) GetUploadStats() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
			return
		}

		// Récupérer les statistiques depuis la base de données
		// Note: Dans un vrai environnement, il faudrait interroger la DB
		stats := map[string]interface{}{
			"total_uploads": 0,
			"total_size":    0,
			"audio_files":   0,
			"image_files":   0,
			"video_files":   0,
		}

		c.JSON(http.StatusOK, gin.H{
			"user_id": userID,
			"stats":   stats,
		})
	}
}

// ValidateFileType valide le type de fichier
func (uh *UploadHandler) ValidateFileType() gin.HandlerFunc {
	return func(c *gin.Context) {
		fileType := c.Query("type")
		if fileType == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "File type parameter required"})
			return
		}

		// Vérifier si le type est supporté
		supportedTypes := []string{"audio", "image", "video"}
		isSupported := false
		for _, supportedType := range supportedTypes {
			if fileType == supportedType {
				isSupported = true
				break
			}
		}

		if !isSupported {
			c.JSON(http.StatusBadRequest, gin.H{
				"error":           "Unsupported file type",
				"supported_types": supportedTypes,
			})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"type":            fileType,
			"supported":       true,
			"supported_types": supportedTypes,
		})
	}
}

// GetUploadLimits récupère les limites d'upload
func (uh *UploadHandler) GetUploadLimits() gin.HandlerFunc {
	return func(c *gin.Context) {
		limits := map[string]interface{}{
			"audio": map[string]interface{}{
				"max_size":       "100MB",
				"max_size_bytes": 100 * 1024 * 1024,
				"allowed_types": []string{
					"audio/mpeg",
					"audio/mp3",
					"audio/wav",
					"audio/flac",
					"audio/aac",
					"audio/ogg",
					"audio/m4a",
				},
			},
			"image": map[string]interface{}{
				"max_size":       "10MB",
				"max_size_bytes": 10 * 1024 * 1024,
				"allowed_types": []string{
					"image/jpeg",
					"image/png",
					"image/gif",
					"image/webp",
					"image/svg+xml",
				},
			},
			"video": map[string]interface{}{
				"max_size":       "500MB",
				"max_size_bytes": 500 * 1024 * 1024,
				"allowed_types": []string{
					"video/mp4",
					"video/webm",
					"video/ogg",
					"video/avi",
				},
			},
		}

		c.JSON(http.StatusOK, gin.H{
			"limits": limits,
		})
	}
}

// UploadProgress gère le suivi de progression d'upload
func (uh *UploadHandler) UploadProgress() gin.HandlerFunc {
	return func(c *gin.Context) {
		uploadIDStr := c.Param("id")
		uploadID, err := uuid.Parse(uploadIDStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid upload ID"})
			return
		}

		// Récupérer la progression depuis la base de données
		// Note: Dans un vrai environnement, il faudrait interroger la DB
		progress := map[string]interface{}{
			"upload_id":                uploadID,
			"status":                   "completed",
			"progress":                 100,
			"bytes_uploaded":           0,
			"total_bytes":              0,
			"estimated_time_remaining": 0,
		}

		c.JSON(http.StatusOK, progress)
	}
}

// BatchUpload gère les uploads multiples
func (uh *UploadHandler) BatchUpload() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
			return
		}

		// Parser le formulaire multipart
		form, err := c.MultipartForm()
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid multipart form"})
			return
		}

		files := form.File["files"]
		if len(files) == 0 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "No files provided"})
			return
		}

		// Limiter le nombre de fichiers par batch
		maxFiles := 10
		if len(files) > maxFiles {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": fmt.Sprintf("Too many files. Maximum %d files per batch", maxFiles),
			})
			return
		}

		var results []map[string]interface{}
		var errors []string

		for i, fileHeader := range files {
			// Déterminer le type de fichier à partir de l'extension
			fileType := uh.uploadValidator.GetFileTypeFromPath(fileHeader.Filename)
			if fileType == "unknown" {
				errors = append(errors, fmt.Sprintf("File %d (%s): Unknown file type", i+1, fileHeader.Filename))
				continue
			}

			// Valider le fichier
			validationResult, err := uh.uploadValidator.ValidateFile(fileHeader, fileType)
			if err != nil {
				errors = append(errors, fmt.Sprintf("File %d (%s): Validation error", i+1, fileHeader.Filename))
				continue
			}

			if !validationResult.Valid {
				errors = append(errors, fmt.Sprintf("File %d (%s): %s", i+1, fileHeader.Filename, validationResult.Error))
				continue
			}

			// Créer le résultat
			result := map[string]interface{}{
				"index":     i + 1,
				"file_name": fileHeader.Filename,
				"file_size": validationResult.FileSize,
				"file_type": validationResult.FileType,
				"checksum":  validationResult.Checksum,
				"status":    "validated",
				"upload_id": uuid.New(),
			}

			results = append(results, result)
		}

		uh.logger.Info("Batch upload processed",
			zap.String("user_id", userID.String()),
			zap.Int("total_files", len(files)),
			zap.Int("successful", len(results)),
			zap.Int("errors", len(errors)),
		)

		c.JSON(http.StatusOK, gin.H{
			"message": "Batch upload processed",
			"results": results,
			"errors":  errors,
			"summary": map[string]interface{}{
				"total_files": len(files),
				"successful":  len(results),
				"errors":      len(errors),
			},
		})
	}
}
