package config

import (
	"encoding/json"
	"log"
	"strings"
	"system-altrak/internal/domain"

	"gorm.io/gorm"
)

const (
	auditActionCreate = "CREATE"
	auditActionUpdate = "UPDATE"
	auditActionDelete = "DELETE"
)

func registerAuditCallbacks(db *gorm.DB) {
	if db == nil {
		return
	}

	_ = db.Callback().Create().After("gorm:create").Register("system_altrak_audit_create", func(tx *gorm.DB) {
		recordAuditLog(tx, auditActionCreate)
	})

	_ = db.Callback().Update().After("gorm:update").Register("system_altrak_audit_update", func(tx *gorm.DB) {
		recordAuditLog(tx, auditActionUpdate)
	})

	_ = db.Callback().Delete().After("gorm:delete").Register("system_altrak_audit_delete", func(tx *gorm.DB) {
		recordAuditLog(tx, auditActionDelete)
	})
}

func recordAuditLog(tx *gorm.DB, action string) {
	if tx == nil || tx.Statement == nil {
		return
	}

	table := strings.TrimSpace(tx.Statement.Table)
	if table == "" {
		return
	}

	if table == "audit_logs" || table == "activity_logs" || table == "login_attempts" || table == "revoked_tokens" {
		return
	}

	if tx.Error != nil {
		return
	}

	payloadRaw, err := json.Marshal(tx.Statement.Dest)
	if err != nil {
		payloadRaw = []byte("{}")
	}

	userID := uint(0)
	if tx.Statement.Context != nil {
		if v := tx.Statement.Context.Value("audit_user_id"); v != nil {
			switch val := v.(type) {
			case uint:
				userID = val
			case int:
				if val > 0 {
					userID = uint(val)
				}
			case int64:
				if val > 0 {
					userID = uint(val)
				}
			}
		}
	}

	audit := domain.AuditLog{
		UserID:     userID,
		Action:     action,
		ModuleName: table,
		Payload:    string(payloadRaw),
	}

	if err := tx.Session(&gorm.Session{SkipHooks: true, NewDB: true}).Create(&audit).Error; err != nil {
		log.Printf("audit callback failed for table %s action %s: %v", table, action, err)
	}
}
