package handlers

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

	apperrors "veza-backend-api/internal/errors"
	"veza-backend-api/internal/services"

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

// AuditServiceInterfaceForAuditHandler defines methods needed for audit handler
type AuditServiceInterfaceForAuditHandler interface {
	SearchLogs(ctx context.Context, req *services.AuditLogSearchRequest) ([]*services.AuditLog, error)
	CountLogs(ctx context.Context, req *services.AuditLogSearchRequest) (int64, error)
	GetUserActivity(ctx context.Context, userID uuid.UUID, limit int) ([]*services.AuditLog, error)
	GetIPActivity(ctx context.Context, ipAddress string, limit int) ([]*services.AuditLog, error)
	GetStats(ctx context.Context, startDate, endDate time.Time) ([]*services.AuditStats, error)
	DetectSuspiciousActivity(ctx context.Context, hours int) ([]*services.SuspiciousActivity, error)
	CleanupOldLogs(ctx context.Context, retentionDays int) (int64, error)
}

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

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

// NewAuditHandlerWithInterface creates a new audit handler with interface (for testing)
func NewAuditHandlerWithInterface(
	auditService AuditServiceInterfaceForAuditHandler,
	logger *zap.Logger,
) *AuditHandler {
	return &AuditHandler{
		auditService: auditService,
		logger:       logger,
	}
}

// auditServiceWrapperForAuditHandler wraps *services.AuditService to implement AuditServiceInterfaceForAuditHandler
type auditServiceWrapperForAuditHandler struct {
	auditService *services.AuditService
}

func (w *auditServiceWrapperForAuditHandler) SearchLogs(ctx context.Context, req *services.AuditLogSearchRequest) ([]*services.AuditLog, error) {
	return w.auditService.SearchLogs(ctx, req)
}

func (w *auditServiceWrapperForAuditHandler) CountLogs(ctx context.Context, req *services.AuditLogSearchRequest) (int64, error) {
	return w.auditService.CountLogs(ctx, req)
}

func (w *auditServiceWrapperForAuditHandler) GetUserActivity(ctx context.Context, userID uuid.UUID, limit int) ([]*services.AuditLog, error) {
	return w.auditService.GetUserActivity(ctx, userID, limit)
}

func (w *auditServiceWrapperForAuditHandler) GetIPActivity(ctx context.Context, ipAddress string, limit int) ([]*services.AuditLog, error) {
	return w.auditService.GetIPActivity(ctx, ipAddress, limit)
}

func (w *auditServiceWrapperForAuditHandler) GetStats(ctx context.Context, startDate, endDate time.Time) ([]*services.AuditStats, error) {
	return w.auditService.GetStats(ctx, startDate, endDate)
}

func (w *auditServiceWrapperForAuditHandler) DetectSuspiciousActivity(ctx context.Context, hours int) ([]*services.SuspiciousActivity, error) {
	return w.auditService.DetectSuspiciousActivity(ctx, hours)
}

func (w *auditServiceWrapperForAuditHandler) CleanupOldLogs(ctx context.Context, retentionDays int) (int64, error) {
	return w.auditService.CleanupOldLogs(ctx, retentionDays)
}

// SearchLogs recherche des logs d'audit
// @Summary      Search audit logs
// @Description  Search and filter audit logs with pagination support. Supports filtering by action, resource, date range, IP address, and user agent.
// @Tags         Audit
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        action      query     string  false  "Filter by action type"
// @Param        resource    query     string  false  "Filter by resource type"
// @Param        resource_id query    string  false  "Filter by resource ID (UUID)"
// @Param        ip_address  query    string  false  "Filter by IP address"
// @Param        user_agent  query    string  false  "Filter by user agent"
// @Param        start_date  query    string  false  "Start date filter (YYYY-MM-DD)"
// @Param        end_date    query    string  false  "End date filter (YYYY-MM-DD)"
// @Param        page        query    int     false  "Page number" default(1)
// @Param        limit       query    int     false  "Items per page" default(20)
// @Param        offset      query    int     false  "Offset for pagination"
// @Success      200  {object}  handlers.APIResponse{data=object{logs=array,pagination=object}}
// @Failure      400  {object}  handlers.APIResponse "Validation error"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /audit/logs [get]
// BE-API-034: Enhanced with better filtering and pagination
func (ah *AuditHandler) SearchLogs() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		userID, ok := GetUserIDUUID(c)
		if !ok {
			return // Erreur déjà envoyée par GetUserIDUUID
		}

		// Parser les paramètres de recherche
		req := &services.AuditLogSearchRequest{
			UserID: &userID, // Par défaut, chercher les logs de l'utilisateur
		}

		// Paramètres optionnels de filtrage
		if action := c.Query("action"); action != "" {
			req.Action = action
		}
		if resource := c.Query("resource"); resource != "" {
			req.Resource = resource
		}
		// BE-API-034: Added resource_id filter
		if resourceIDStr := c.Query("resource_id"); resourceIDStr != "" {
			if resourceID, err := uuid.Parse(resourceIDStr); err == nil {
				req.ResourceID = &resourceID
			}
		}
		// BE-API-034: Added IP address filter
		if ipAddress := c.Query("ip_address"); ipAddress != "" {
			req.IPAddress = ipAddress
		}
		// BE-API-034: Added user_agent filter
		if userAgent := c.Query("user_agent"); userAgent != "" {
			req.UserAgent = userAgent
		}
		if startDateStr := c.Query("start_date"); startDateStr != "" {
			if startDate, err := time.Parse("2006-01-02", startDateStr); err == nil {
				req.StartDate = &startDate
			} else {
				RespondWithAppError(c, apperrors.NewValidationError("Invalid start_date format. Use YYYY-MM-DD"))
				return
			}
		}
		if endDateStr := c.Query("end_date"); endDateStr != "" {
			if endDate, err := time.Parse("2006-01-02", endDateStr); err == nil {
				req.EndDate = &endDate
			} else {
				RespondWithAppError(c, apperrors.NewValidationError("Invalid end_date format. Use YYYY-MM-DD"))
				return
			}
		}

		// BE-API-034: Improved pagination - support both page/limit and offset/limit
		if pageStr := c.Query("page"); pageStr != "" {
			if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
				req.Page = page
			} else {
				RespondWithAppError(c, apperrors.NewValidationError("Invalid page number. Must be a positive integer"))
				return
			}
		}
		if limitStr := c.Query("limit"); limitStr != "" {
			if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 && limit <= 100 {
				req.Limit = limit
			} else {
				RespondWithAppError(c, apperrors.NewValidationError("Invalid limit. Must be between 1 and 100"))
				return
			}
		} else {
			req.Limit = 50 // Limite par défaut
		}
		if offsetStr := c.Query("offset"); offsetStr != "" {
			if offset, err := strconv.Atoi(offsetStr); err == nil && offset >= 0 {
				req.Offset = offset
			} else {
				RespondWithAppError(c, apperrors.NewValidationError("Invalid offset. Must be a non-negative integer"))
				return
			}
		}

		// 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()),
			)
			RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to search audit logs", err))
			return
		}

		// BE-API-034: Get total count for pagination
		total, err := ah.auditService.CountLogs(c.Request.Context(), req)
		if err != nil {
			ah.logger.Warn("Failed to count audit logs, continuing without total",
				zap.Error(err),
			)
			total = int64(len(logs)) // Fallback to current count
		}

		// INT-007: Standardize pagination format
		var pagination PaginationData
		if req.Page > 0 {
			pagination = BuildPaginationData(req.Page, req.Limit, total)
		} else {
			// Pour offset-based, on calcule la page approximative
			page := (req.Offset / req.Limit) + 1
			pagination = BuildPaginationData(page, req.Limit, total)
		}

		RespondSuccess(c, http.StatusOK, gin.H{
			"logs":       logs,
			"count":      len(logs),
			"pagination": pagination,
		})
	}
}

// GetStats récupère les statistiques d'audit
// @Summary      Get audit statistics
// @Description  Get audit statistics for the current user, optionally filtered by date range
// @Tags         Audit
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        start_date  query    string  false  "Start date (YYYY-MM-DD)"
// @Param        end_date    query    string  false  "End date (YYYY-MM-DD)"
// @Success      200  {object}  handlers.APIResponse{data=object{stats=object}}
// @Failure      400  {object}  handlers.APIResponse "Validation error"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /audit/stats [get]
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 {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "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 {
				// Action 1.3.2.1: Use wrapped format helper for errors
				RespondWithError(c, int(apperrors.ErrCodeValidation), "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 {
				// Action 1.3.2.1: Use wrapped format helper for errors
				RespondWithError(c, int(apperrors.ErrCodeValidation), "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()),
			)
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to get audit stats")
			return
		}

		// Action 1.3.2.1: Use wrapped format helper
		RespondSuccess(c, http.StatusOK, gin.H{
			"user_id":    userID,
			"start_date": startDate,
			"end_date":   endDate,
			"stats":      stats,
		})
	}
}

// GetUserActivity récupère l'activité d'un utilisateur
// @Summary      Get user activity
// @Description  Get recent activity logs for the current user
// @Tags         Audit
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        limit  query     int     false "Number of activities to return" default(50)
// @Success      200  {object}  handlers.APIResponse{data=object{activities=array}}
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /audit/activity [get]
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 {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "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()),
			)
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to get user activity")
			return
		}

		// Action 1.3.2.1: Use wrapped format helper
		RespondSuccess(c, 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 {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "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()),
			)
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to detect suspicious activity")
			return
		}

		// Action 1.3.2.1: Use wrapped format helper
		RespondSuccess(c, 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 {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "Invalid user ID type")
			return
		}

		// Récupérer l'IP depuis les paramètres
		ipAddress := c.Param("ip")
		if ipAddress == "" {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeValidation), "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),
			)
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to get IP activity")
			return
		}

		// Action 1.3.2.1: Use wrapped format helper
		RespondSuccess(c, 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 {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "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()),
			)
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "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),
		)

		// Action 1.3.2.1: Use wrapped format helper
		RespondSuccess(c, 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 {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "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 {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeValidation), "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()),
			)
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to get audit log")
			return
		}

		if len(logs) == 0 {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeNotFound), "Audit log not found")
			return
		}

		// Vérifier que le log appartient à l'utilisateur
		log := logs[0]
		if log.UserID != nil && *log.UserID != userID {
			// Action 1.3.2.1: Use wrapped format helper for errors
			RespondWithError(c, int(apperrors.ErrCodeForbidden), "Access denied")
			return
		}

		// Action 1.3.2.1: Use wrapped format helper
		RespondSuccess(c, http.StatusOK, gin.H{
			"log": log,
		})
	}
}
