package controllers

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

	"fotbal-club/internal/models"
	"fotbal-club/pkg/logger"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// AuditLogController handles audit log operations
type AuditLogController struct {
	DB *gorm.DB
}

// NewAuditLogController creates a new AuditLogController instance
func NewAuditLogController(db *gorm.DB) *AuditLogController {
	return &AuditLogController{DB: db}
}

// AuditAction represents common audit actions
const (
	AuditActionCreate = "CREATE"
	AuditActionUpdate = "UPDATE"
	AuditActionDelete = "DELETE"
	AuditActionLogin  = "LOGIN"
	AuditActionLogout = "LOGOUT"
	AuditActionView   = "VIEW"
	AuditActionExport = "EXPORT"
)

// LogEntry creates an audit log entry
func (alc *AuditLogController) LogEntry(c *gin.Context, action, entityType string, entityID *uint, description string, changes interface{}) error {
	var userID *uint
	if user, exists := c.Get("user"); exists {
		if u, ok := user.(*models.User); ok && u != nil {
			userID = &u.ID
		}
	}

	// Serialize changes to JSON
	var changesJSON string
	if changes != nil {
		if b, err := json.Marshal(changes); err == nil {
			changesJSON = string(b)
		}
	}

	log := models.AuditLog{
		UserID:      userID,
		Action:      action,
		EntityType:  entityType,
		EntityID:    entityID,
		Description: description,
		IPAddress:   c.ClientIP(),
		UserAgent:   c.Request.UserAgent(),
		Changes:     changesJSON,
		CreatedAt:   time.Now(),
	}

	if err := alc.DB.Create(&log).Error; err != nil {
		logger.Error("Failed to create audit log: %v", err)
		return err
	}

	return nil
}

// LogCreate logs a CREATE action
func (alc *AuditLogController) LogCreate(c *gin.Context, entityType string, entityID uint, description string) error {
	id := entityID
	return alc.LogEntry(c, AuditActionCreate, entityType, &id, description, nil)
}

// LogUpdate logs an UPDATE action with before/after changes
func (alc *AuditLogController) LogUpdate(c *gin.Context, entityType string, entityID uint, description string, before, after interface{}) error {
	id := entityID
	changes := map[string]interface{}{
		"before": before,
		"after":  after,
	}
	return alc.LogEntry(c, AuditActionUpdate, entityType, &id, description, changes)
}

// LogDelete logs a DELETE action
func (alc *AuditLogController) LogDelete(c *gin.Context, entityType string, entityID uint, description string) error {
	id := entityID
	return alc.LogEntry(c, AuditActionDelete, entityType, &id, description, nil)
}

// LogLogin logs a login action
func (alc *AuditLogController) LogLogin(c *gin.Context, userID uint, success bool) error {
	id := userID
	description := "User logged in successfully"
	if !success {
		description = "Failed login attempt"
	}
	return alc.LogEntry(c, AuditActionLogin, "User", &id, description, nil)
}

// LogLogout logs a logout action
func (alc *AuditLogController) LogLogout(c *gin.Context, userID uint) error {
	id := userID
	return alc.LogEntry(c, AuditActionLogout, "User", &id, "User logged out", nil)
}

// GetAuditLogs retrieves audit logs with filtering and pagination (admin only)
func (alc *AuditLogController) GetAuditLogs(c *gin.Context) {
	query := alc.DB.Model(&models.AuditLog{}).Preload("User")

	// Apply filters
	query = QueryParser.BuildQueryChain(c, query).
		WithSearch("action", "entity_type", "description").
		WithDateRange("created_at").
		Build()

	// Filter by action
	if action := c.Query("action"); action != "" {
		query = query.Where("action = ?", action)
	}

	// Filter by entity type
	if entityType := c.Query("entity_type"); entityType != "" {
		query = query.Where("entity_type = ?", entityType)
	}

	// Filter by user ID
	if userID := c.Query("user_id"); userID != "" {
		query = query.Where("user_id = ?", userID)
	}

	// Apply sorting (default: newest first)
	query = QueryParser.ApplySortFromContext(c, query, "created_at", "desc")

	// Paginate
	var logs []models.AuditLog
	meta, err := Paginator.Paginate(c, query, &logs)
	if err != nil {
		Respond.InternalError(c, "Failed to retrieve audit logs")
		return
	}

	Respond.SuccessWithMeta(c, logs, meta, "Audit logs retrieved successfully")
}

// GetAuditLogByID retrieves a single audit log by ID (admin only)
func (alc *AuditLogController) GetAuditLogByID(c *gin.Context) {
	id := c.Param("id")

	var log models.AuditLog
	if err := alc.DB.Preload("User").First(&log, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			Respond.NotFound(c, "Audit log not found")
			return
		}
		Respond.InternalError(c, "Failed to retrieve audit log")
		return
	}

	Respond.Success(c, log, "Audit log retrieved successfully")
}

// GetEntityAuditHistory retrieves audit history for a specific entity
func (alc *AuditLogController) GetEntityAuditHistory(c *gin.Context) {
	entityType := c.Param("entity_type")
	entityID := c.Param("entity_id")

	query := alc.DB.Model(&models.AuditLog{}).
		Where("entity_type = ? AND entity_id = ?", entityType, entityID).
		Preload("User").
		Order("created_at DESC")

	var logs []models.AuditLog
	meta, err := Paginator.Paginate(c, query, &logs)
	if err != nil {
		Respond.InternalError(c, "Failed to retrieve audit history")
		return
	}

	Respond.SuccessWithMeta(c, logs, meta, fmt.Sprintf("Audit history for %s #%s retrieved successfully", entityType, entityID))
}

// GetUserActivityLog retrieves activity log for a specific user
func (alc *AuditLogController) GetUserActivityLog(c *gin.Context) {
	userID := c.Param("user_id")

	query := alc.DB.Model(&models.AuditLog{}).
		Where("user_id = ?", userID).
		Preload("User").
		Order("created_at DESC")

	var logs []models.AuditLog
	meta, err := Paginator.Paginate(c, query, &logs)
	if err != nil {
		Respond.InternalError(c, "Failed to retrieve user activity")
		return
	}

	Respond.SuccessWithMeta(c, logs, meta, "User activity log retrieved successfully")
}

// GetAuditStats returns audit statistics (admin only)
func (alc *AuditLogController) GetAuditStats(c *gin.Context) {
	var stats struct {
		TotalLogs     int64                  `json:"total_logs"`
		ActionCounts  map[string]int64       `json:"action_counts"`
		EntityCounts  map[string]int64       `json:"entity_counts"`
		RecentActions []models.AuditLog      `json:"recent_actions"`
	}

	// Total logs
	alc.DB.Model(&models.AuditLog{}).Count(&stats.TotalLogs)

	// Action counts
	stats.ActionCounts = make(map[string]int64)
	var actionCounts []struct {
		Action string `json:"action"`
		Count  int64  `json:"count"`
	}
	alc.DB.Model(&models.AuditLog{}).
		Select("action, COUNT(*) as count").
		Group("action").
		Scan(&actionCounts)
	for _, ac := range actionCounts {
		stats.ActionCounts[ac.Action] = ac.Count
	}

	// Entity counts
	stats.EntityCounts = make(map[string]int64)
	var entityCounts []struct {
		EntityType string `json:"entity_type"`
		Count      int64  `json:"count"`
	}
	alc.DB.Model(&models.AuditLog{}).
		Select("entity_type, COUNT(*) as count").
		Where("entity_type IS NOT NULL AND entity_type != ''").
		Group("entity_type").
		Scan(&entityCounts)
	for _, ec := range entityCounts {
		stats.EntityCounts[ec.EntityType] = ec.Count
	}

	// Recent actions
	alc.DB.Model(&models.AuditLog{}).
		Preload("User").
		Order("created_at DESC").
		Limit(10).
		Find(&stats.RecentActions)

	c.JSON(http.StatusOK, stats)
}

// CleanupOldLogs deletes audit logs older than specified days (admin only)
func (alc *AuditLogController) CleanupOldLogs(c *gin.Context) {
	var req struct {
		DaysOld int `json:"days_old" binding:"required,min=30"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid request: days_old must be at least 30")
		return
	}

	cutoffDate := time.Now().AddDate(0, 0, -req.DaysOld)

	result := alc.DB.Where("created_at < ?", cutoffDate).Delete(&models.AuditLog{})
	if result.Error != nil {
		logger.Error("Failed to cleanup old audit logs: %v", result.Error)
		Respond.InternalError(c, "Failed to cleanup old audit logs")
		return
	}

	logger.Info("Cleaned up %d audit logs older than %d days", result.RowsAffected, req.DaysOld)
	Respond.Success(c, gin.H{
		"deleted_count": result.RowsAffected,
		"cutoff_date":   cutoffDate,
	}, fmt.Sprintf("Successfully deleted %d audit logs older than %d days", result.RowsAffected, req.DaysOld))
}

// Global AuditLogController instance (needs to be initialized with DB)
var AuditLogger *AuditLogController

// InitAuditLogger initializes the global audit logger
func InitAuditLogger(db *gorm.DB) {
	AuditLogger = NewAuditLogController(db)
}
