package repository

import (
	"context"
	"strings"
	"time"

	"gorm.io/gorm"

	"security-center-backend/internal/model"
)

type AuditFilter struct {
	UserID       *uint64
	ActorEmail   string
	ActorType    string
	DepartmentID string
	ResourceType string
	Action       string
	From         *time.Time
	To           *time.Time
	Keyword      string
}

type AuditRepository interface {
	Create(ctx context.Context, log *model.AuditLog) error
	List(ctx context.Context, filter AuditFilter, page, pageSize int) ([]model.AuditLog, int64, error)
	FindByID(ctx context.Context, id uint64) (*model.AuditLog, error)
	Stats(ctx context.Context) (*AuditStats, error)
}

type AuditStats struct {
	Total      int64       `json:"total"`
	Last24h    int64       `json:"last_24h"`
	Modules    []CountStat `json:"modules"`
	TopActions []CountStat `json:"actions"`
}

type CountStat struct {
	Name  string `json:"name"`
	Count int64  `json:"count"`
}

type GormAuditRepository struct {
	db *gorm.DB
}

func NewAuditRepository(db *gorm.DB) AuditRepository {
	return &GormAuditRepository{db: db}
}

func (r *GormAuditRepository) Create(ctx context.Context, log *model.AuditLog) error {
	return r.db.WithContext(ctx).Create(log).Error
}

func (r *GormAuditRepository) List(ctx context.Context, filter AuditFilter, page, pageSize int) ([]model.AuditLog, int64, error) {
	var (
		logs  []model.AuditLog
		total int64
	)
	query := r.db.WithContext(ctx).Model(&model.AuditLog{})
	if filter.UserID != nil {
		query = query.Where("(user_id = ? OR actor_user_id = ?)", *filter.UserID, *filter.UserID)
	}
	if filter.ActorEmail != "" {
		query = query.Where("actor_email = ?", filter.ActorEmail)
	}
	if filter.ActorType != "" {
		query = query.Where("audit_logs.actor_type = ?", filter.ActorType)
	}
	if filter.DepartmentID != "" || strings.TrimSpace(filter.Keyword) != "" {
		query = query.
			Joins("LEFT JOIN users actor_users ON actor_users.id = audit_logs.actor_user_id").
			Joins("LEFT JOIN employees linked_employees ON linked_employees.id = actor_users.employee_id").
			Joins("LEFT JOIN employees email_employees ON LOWER(email_employees.email) = LOWER(audit_logs.actor_email)")
	}
	if filter.ResourceType != "" {
		query = query.Where("audit_logs.resource_type = ?", filter.ResourceType)
	}
	if filter.Action != "" {
		query = query.Where("audit_logs.action = ?", filter.Action)
	}
	if filter.DepartmentID != "" {
		query = query.Where("(linked_employees.department_id = ? OR (actor_users.id IS NULL AND email_employees.department_id = ?))", filter.DepartmentID, filter.DepartmentID)
	}
	if filter.From != nil {
		query = query.Where("audit_logs.created_at >= ?", *filter.From)
	}
	if filter.To != nil {
		query = query.Where("audit_logs.created_at <= ?", *filter.To)
	}
	if filter.Keyword != "" {
		keyword := strings.ToLower(strings.TrimSpace(filter.Keyword))
		if keyword != "" {
			like := "%" + keyword + "%"
			query = query.Where(
				`(
					LOWER(audit_logs.action) LIKE ?
					OR LOWER(audit_logs.resource_id) LIKE ?
					OR LOWER(audit_logs.ip_address) LIKE ?
					OR LOWER(audit_logs.user_agent) LIKE ?
					OR LOWER(audit_logs.actor_email) LIKE ?
					OR LOWER(REPLACE(audit_logs.resource_type, '_', ' ')) LIKE ?
					OR LOWER(CAST(audit_logs.metadata AS CHAR)) LIKE ?
					OR LOWER(CAST(audit_logs.before_state AS CHAR)) LIKE ?
					OR LOWER(CAST(audit_logs.after_state AS CHAR)) LIKE ?
					OR LOWER(actor_users.name) LIKE ?
					OR LOWER(linked_employees.name) LIKE ?
					OR LOWER(email_employees.name) LIKE ?
				)`,
				like, like, like, like, like, like, like, like, like, like, like, like,
			)
		}
	}
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.
		Preload("ActorUser").
		Preload("ActorUser.Employee").
		Order("audit_logs.created_at desc").
		Limit(pageSize).
		Offset((page - 1) * pageSize).
		Find(&logs).Error; err != nil {
		return nil, 0, err
	}
	return logs, total, nil
}

func (r *GormAuditRepository) FindByID(ctx context.Context, id uint64) (*model.AuditLog, error) {
	var log model.AuditLog
	if err := r.db.WithContext(ctx).
		Preload("ActorUser").
		Preload("ActorUser.Employee").
		First(&log, id).Error; err != nil {
		return nil, err
	}
	return &log, nil
}

func (r *GormAuditRepository) Stats(ctx context.Context) (*AuditStats, error) {
	stats := &AuditStats{}
	if err := r.db.WithContext(ctx).Model(&model.AuditLog{}).Count(&stats.Total).Error; err != nil {
		return nil, err
	}
	cutoff := time.Now().Add(-24 * time.Hour)
	if err := r.db.WithContext(ctx).Model(&model.AuditLog{}).Where("created_at >= ?", cutoff).Count(&stats.Last24h).Error; err != nil {
		return nil, err
	}
	var moduleRows []CountStat
	if err := r.db.WithContext(ctx).
		Model(&model.AuditLog{}).
		Select("resource_type as name, COUNT(*) as count").
		Group("resource_type").
		Order("count desc").
		Limit(5).
		Scan(&moduleRows).Error; err != nil {
		return nil, err
	}
	stats.Modules = moduleRows
	var actionRows []CountStat
	if err := r.db.WithContext(ctx).
		Model(&model.AuditLog{}).
		Select("action as name, COUNT(*) as count").
		Group("action").
		Order("count desc").
		Limit(5).
		Scan(&actionRows).Error; err != nil {
		return nil, err
	}
	stats.TopActions = actionRows
	return stats, nil
}
