package mysql

import (
	"context"
	"fmt"
	"strings"

	"gorm.io/gorm"

	"wucher/internal/domain/audit"
)

type AuditRepository struct {
	db *gorm.DB
}

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

func (r *AuditRepository) Create(ctx context.Context, entry *audit.AuditLog) error {
	return r.db.WithContext(ctx).Create(entry).Error
}

func (r *AuditRepository) List(ctx context.Context, filter string, methods []string, modules []string, actions []string, sort string, limit, offset int) ([]audit.AuditLog, int64, error) {
	q := r.db.WithContext(ctx).Model(&audit.AuditLog{})
	filter = strings.TrimSpace(filter)
	if filter != "" {
		like := "%" + strings.ToLower(filter) + "%"
		q = q.Where(
			`LOWER(request_id) LIKE ? OR LOWER(layer) LIKE ? OR LOWER(action) LIKE ? OR LOWER(method) LIKE ? OR LOWER(path) LIKE ? OR LOWER(message) LIKE ?`,
			like, like, like, like, like, like,
		)
	}
	if len(methods) > 0 {
		q = q.Where("UPPER(method) IN ?", methods)
	}
	if len(modules) > 0 {
		pathPrefixes := modulePathPrefixes(modules)
		if len(pathPrefixes) == 0 {
			q = q.Where("LOWER(module) IN ?", modules)
		} else {
			pathConds := make([]string, 0, len(pathPrefixes))
			pathArgs := make([]any, 0, len(pathPrefixes))
			for _, prefix := range pathPrefixes {
				pathConds = append(pathConds, "LOWER(path) LIKE ?")
				pathArgs = append(pathArgs, strings.ToLower(prefix)+"%")
			}
			cond := fmt.Sprintf("(LOWER(module) IN ? OR ((module IS NULL OR module = '') AND (%s)))", strings.Join(pathConds, " OR "))
			args := make([]any, 0, 2+len(pathArgs))
			args = append(args, modules)
			args = append(args, pathArgs...)
			q = q.Where(cond, args...)
		}
	}
	if len(actions) > 0 {
		q = q.Where("LOWER(action) IN ?", actions)
	}

	var total int64
	if err := q.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	if strings.TrimSpace(sort) == "" {
		sort = "created_at DESC"
	}
	if limit < 0 {
		limit = 0
	}
	if limit > 200 {
		limit = 200
	}
	if offset < 0 {
		offset = 0
	}

	var out []audit.AuditLog
	query := q.Order(sort)
	if limit > 0 {
		query = query.Limit(limit).Offset(offset)
	}
	if err := query.Find(&out).Error; err != nil {
		return nil, 0, err
	}
	return out, total, nil
}

func modulePathPrefixes(modules []string) []string {
	seen := map[string]struct{}{}
	out := make([]string, 0, len(modules))
	for _, module := range modules {
		var prefix string
		switch strings.ToLower(strings.TrimSpace(module)) {
		case "air_rescuer_checklist":
			prefix = "/api/v1/air-rescuer-checklist/"
		case "audit_log":
			prefix = "/api/v1/audit-logs/"
		case "auth":
			prefix = "/api/v1/auth/"
		case "base":
			prefix = "/api/v1/bases/"
		case "branding":
			prefix = "/api/v1/branding/"
		case "contact":
			prefix = "/api/v1/contacts/"
		case "duty_roster":
			prefix = "/api/v1/duty-roster/"
		case "dul":
			prefix = "/api/v1/dul/"
		case "facility":
			prefix = "/api/v1/facilities/"
		case "federal_state":
			prefix = "/api/v1/federal-state/"
		case "file_manager":
			prefix = "/api/v1/file-manager/"
		case "flight_data":
			prefix = "/api/v1/flight-data/"
		case "flight":
			prefix = "/api/v1/flights/"
		case "forces_present":
			prefix = "/api/v1/forces-present/"
		case "health_insurance_company":
			prefix = "/api/v1/health-insurance-companies/"
		case "helicopter_file":
			prefix = "/api/v1/helicopter-files/"
		case "helicopter":
			prefix = "/api/v1/helicopters/"
		case "hospital":
			prefix = "/api/v1/hospital/"
		case "icao":
			prefix = "/api/v1/icao/"
		case "insurance_patient_data":
			prefix = "/api/v1/insurance-patient-data/"
		case "land":
			prefix = "/api/v1/land/"
		case "master_setting":
			prefix = "/api/v1/master-settings/"
		case "medicine":
			prefix = "/api/v1/medicine/"
		case "mission":
			prefix = "/api/v1/mission/"
		case "opc":
			prefix = "/api/v1/opc/"
		case "patient_data":
			prefix = "/api/v1/patient-data/"
		case "reserve_ac":
			prefix = "/api/v1/reserve-acs/"
		case "role":
			prefix = "/api/v1/roles/"
		case "user":
			prefix = "/api/v1/users/"
		case "vocation":
			prefix = "/api/v1/vocation/"
		}
		if prefix == "" {
			continue
		}
		if _, ok := seen[prefix]; ok {
			continue
		}
		seen[prefix] = struct{}{}
		out = append(out, prefix)
	}
	return out
}
