package handlers

import (
	"net/http"
	"strconv"
	"time"

	"veza-backend-api/internal/services"

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

// AuditHandler gère les opérations sur les logs d'audit
type AuditHandler struct {
	auditService *services.AuditService
	logger       *zap.Logger
}

// NewAuditHandler crée un nouveau handler d'audit
func NewAuditHandler(
	auditService *services.AuditService,
	logger *zap.Logger,
) *AuditHandler {
	return &AuditHandler{
		auditService: auditService,
		logger:       logger,
	}
}

// SearchLogs recherche des logs d'audit
func (ah *AuditHandler) SearchLogs() 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 les paramètres de recherche
		req := &services.AuditLogSearchRequest{
			UserID: &userID, // Par défaut, chercher les logs de l'utilisateur
		}

		// Paramètres optionnels
		if action := c.Query("action"); action != "" {
			req.Action = action
		}
		if resource := c.Query("resource"); resource != "" {
			req.Resource = resource
		}
		if startDateStr := c.Query("start_date"); startDateStr != "" {
			if startDate, err := time.Parse("2006-01-02", startDateStr); err == nil {
				req.StartDate = &startDate
			}
		}
		if endDateStr := c.Query("end_date"); endDateStr != "" {
			if endDate, err := time.Parse("2006-01-02", endDateStr); err == nil {
				req.EndDate = &endDate
			}
		}
		if limitStr := c.Query("limit"); limitStr != "" {
			if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 && limit <= 100 {
				req.Limit = limit
			} else {
				req.Limit = 50 // Limite par défaut
			}
		} else {
			req.Limit = 50
		}
		if offsetStr := c.Query("offset"); offsetStr != "" {
			if offset, err := strconv.Atoi(offsetStr); err == nil && offset >= 0 {
				req.Offset = offset
			}
		}

		// Effectuer la recherche
		logs, err := ah.auditService.SearchLogs(c.Request.Context(), req)
		if err != nil {
			ah.logger.Error("Failed to search audit logs",
				zap.Error(err),
				zap.String("user_id", userID.String()),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to search audit logs"})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"logs":  logs,
			"count": len(logs),
			"query": req,
		})
	}
}

// GetStats récupère les statistiques d'audit
func (ah *AuditHandler) GetStats() 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 les paramètres de date
		var startDate, endDate time.Time
		var err error

		if startDateStr := c.Query("start_date"); startDateStr != "" {
			startDate, err = time.Parse("2006-01-02", startDateStr)
			if err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid start_date format"})
				return
			}
		} else {
			startDate = time.Now().AddDate(0, 0, -30) // 30 jours par défaut
		}

		if endDateStr := c.Query("end_date"); endDateStr != "" {
			endDate, err = time.Parse("2006-01-02", endDateStr)
			if err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid end_date format"})
				return
			}
		} else {
			endDate = time.Now()
		}

		// Récupérer les statistiques
		stats, err := ah.auditService.GetStats(c.Request.Context(), startDate, endDate)
		if err != nil {
			ah.logger.Error("Failed to get audit stats",
				zap.Error(err),
				zap.String("user_id", userID.String()),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get audit stats"})
			return
		}

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

// GetUserActivity récupère l'activité d'un utilisateur
func (ah *AuditHandler) GetUserActivity() 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 paramètre limit
		limit := 50 // Limite par défaut
		if limitStr := c.Query("limit"); limitStr != "" {
			if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
				limit = parsedLimit
			}
		}

		// Récupérer l'activité
		activity, err := ah.auditService.GetUserActivity(c.Request.Context(), userID, limit)
		if err != nil {
			ah.logger.Error("Failed to get user activity",
				zap.Error(err),
				zap.String("user_id", userID.String()),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user activity"})
			return
		}

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

// DetectSuspiciousActivity détecte les activités suspectes
func (ah *AuditHandler) DetectSuspiciousActivity() 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 paramètre hours
		hours := 24 // 24 heures par défaut
		if hoursStr := c.Query("hours"); hoursStr != "" {
			if parsedHours, err := strconv.Atoi(hoursStr); err == nil && parsedHours > 0 && parsedHours <= 168 {
				hours = parsedHours
			}
		}

		// Détecter les activités suspectes
		activities, err := ah.auditService.DetectSuspiciousActivity(c.Request.Context(), hours)
		if err != nil {
			ah.logger.Error("Failed to detect suspicious activity",
				zap.Error(err),
				zap.String("user_id", userID.String()),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to detect suspicious activity"})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"user_id":    userID,
			"hours":      hours,
			"activities": activities,
			"count":      len(activities),
		})
	}
}

// GetIPActivity récupère l'activité d'une IP
func (ah *AuditHandler) GetIPActivity() 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 l'IP depuis les paramètres
		ipAddress := c.Param("ip")
		if ipAddress == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "IP address parameter required"})
			return
		}

		// Parser le paramètre limit
		limit := 50 // Limite par défaut
		if limitStr := c.Query("limit"); limitStr != "" {
			if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
				limit = parsedLimit
			}
		}

		// Récupérer l'activité de l'IP
		activity, err := ah.auditService.GetIPActivity(c.Request.Context(), ipAddress, limit)
		if err != nil {
			ah.logger.Error("Failed to get IP activity",
				zap.Error(err),
				zap.String("user_id", userID.String()),
				zap.String("ip_address", ipAddress),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get IP activity"})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"user_id":    userID,
			"ip_address": ipAddress,
			"activity":   activity,
			"count":      len(activity),
		})
	}
}

// CleanupOldLogs nettoie les anciens logs d'audit
func (ah *AuditHandler) CleanupOldLogs() 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 paramètre retention_days
		retentionDays := 90 // 90 jours par défaut
		if retentionStr := c.Query("retention_days"); retentionStr != "" {
			if parsedRetention, err := strconv.Atoi(retentionStr); err == nil && parsedRetention > 0 && parsedRetention <= 365 {
				retentionDays = parsedRetention
			}
		}

		// Nettoyer les anciens logs
		deletedCount, err := ah.auditService.CleanupOldLogs(c.Request.Context(), retentionDays)
		if err != nil {
			ah.logger.Error("Failed to cleanup old audit logs",
				zap.Error(err),
				zap.String("user_id", userID.String()),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to cleanup old logs"})
			return
		}

		ah.logger.Info("Old audit logs cleaned up",
			zap.String("user_id", userID.String()),
			zap.Int64("deleted_count", deletedCount),
			zap.Int("retention_days", retentionDays),
		)

		c.JSON(http.StatusOK, gin.H{
			"message":        "Old audit logs cleaned up successfully",
			"deleted_count":  deletedCount,
			"retention_days": retentionDays,
		})
	}
}

// GetAuditLog récupère un log d'audit spécifique
func (ah *AuditHandler) GetAuditLog() 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 l'ID du log depuis les paramètres
		logIDStr := c.Param("id")
		logID, err := uuid.Parse(logIDStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid log ID"})
			return
		}

		// Rechercher le log spécifique
		req := &services.AuditLogSearchRequest{
			UserID: &userID,
			Limit:  1,
		}

		logs, err := ah.auditService.SearchLogs(c.Request.Context(), req)
		if err != nil {
			ah.logger.Error("Failed to get audit log",
				zap.Error(err),
				zap.String("user_id", userID.String()),
				zap.String("log_id", logID.String()),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get audit log"})
			return
		}

		if len(logs) == 0 {
			c.JSON(http.StatusNotFound, gin.H{"error": "Audit log not found"})
			return
		}

		// Vérifier que le log appartient à l'utilisateur
		log := logs[0]
		if log.UserID != nil && *log.UserID != userID {
			c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
			return
		}

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