// internal/controllers/admin_controller.go
package controllers

import (
	"lost-and-found/internal/repositories"
	"lost-and-found/internal/services"
	"lost-and-found/internal/utils"
	"net/http"
	"strconv"

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

type AdminController struct {
	DB               *gorm.DB // ✅ DITAMBAHKAN: Untuk akses transaksi manual (Begin/Commit)
	userService      *services.UserService
	itemRepo         *repositories.ItemRepository
	claimRepo        *repositories.ClaimRepository
	archiveService   *services.ArchiveService // ✅ Service Archive
	auditService     *services.AuditService
	dashboardService *services.DashboardService
	itemService      *services.ItemService
}

func NewAdminController(db *gorm.DB) *AdminController {
	return &AdminController{
		DB:               db, // ✅ Simpan koneksi DB
		userService:      services.NewUserService(db),
		itemRepo:         repositories.NewItemRepository(db),
		claimRepo:        repositories.NewClaimRepository(db),
		archiveService:   services.NewArchiveService(db),
		auditService:     services.NewAuditService(db),
		dashboardService: services.NewDashboardService(db),
		itemService:      services.NewItemService(db),
	}
}

// GetDashboardStats - ✅ MENGGUNAKAN VIEW vw_dashboard_stats
// GET /api/admin/dashboard
func (c *AdminController) GetDashboardStats(ctx *gin.Context) {
	// 1. Ambil stats dasar dari View
	dashStats, err := c.dashboardService.GetDashboardStats()
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get dashboard stats", err.Error())
		return
	}

	// 2. Hitung Total User
	allUsers, totalUsers, _ := c.userService.GetAllUsers(1, 10000)

	// 3. Hitung Total Items
	totalItems, _ := c.itemRepo.CountAll()

	// 4. Hitung Total Claims
	totalClaims, _ := c.claimRepo.CountAll()

	// 5. Ambil Statistik Arsip
	archiveStats, err := c.archiveService.GetArchiveStats()
	var totalArchives int64 = 0

	// Type assertion yang aman
	if err == nil {
		if val, ok := archiveStats["total"]; ok {
			switch v := val.(type) {
			case int:
				totalArchives = int64(v)
			case int64:
				totalArchives = v
			case float64:
				totalArchives = int64(v)
			}
		}
	}

	// 6. Ambil Category Stats
	categoryStats, _ := c.dashboardService.GetCategoryStats()

	_, totalAuditLogs, _ := c.auditService.GetAllAuditLogs(1, 1, "", "", nil)

	// 7. Susun Response
	stats := map[string]interface{}{
		"items": map[string]interface{}{
			"total":     totalItems,
			"unclaimed": dashStats.TotalUnclaimed,
			"verified":  dashStats.TotalVerified,
		},
		"claims": map[string]interface{}{
			"total":   totalClaims,
			"pending": dashStats.PendingClaims,
		},
		"archives": map[string]interface{}{
			"total": totalArchives,
		},
		"lost_items": map[string]interface{}{
			"total": dashStats.TotalLostReports,
		},
		"matches": map[string]interface{}{
			"unnotified": dashStats.UnnotifiedMatches,
		},
		"users": map[string]interface{}{
			"total": totalUsers,
			"count": len(allUsers),
		},
		"categories": categoryStats,
		"audit_logs": map[string]interface{}{
			"total": totalAuditLogs,
		},
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Dashboard stats retrieved", stats)
}

// GetAuditLogs gets audit logs (admin only)
// GET /api/admin/audit-logs
func (c *AdminController) GetAuditLogs(ctx *gin.Context) {
	page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "20"))
	action := ctx.Query("action")
	entityType := ctx.Query("entity_type")

	var userID *uint
	if userIDStr := ctx.Query("user_id"); userIDStr != "" {
		id, _ := strconv.ParseUint(userIDStr, 10, 32)
		userID = new(uint)
		*userID = uint(id)
	}

	logs, total, err := c.auditService.GetAllAuditLogs(page, limit, action, entityType, userID)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get audit logs", err.Error())
		return
	}

	utils.SendPaginatedResponse(ctx, http.StatusOK, "Audit logs retrieved", logs, total, page, limit)
}

// GetItemsDetail - Get Items with Details (PAKAI VIEW)
// GET /api/admin/items-detail
func (c *AdminController) GetItemsDetail(ctx *gin.Context) {
	page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
	status := ctx.Query("status")

	items, total, err := c.dashboardService.GetItemsWithDetails(page, limit, status)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get items detail", err.Error())
		return
	}

	utils.SendPaginatedResponse(ctx, http.StatusOK, "Items detail retrieved", items, total, page, limit)
}

// =========================================================================
// OPSI 1: ARSIP VIA STORED PROCEDURE (Database Logic)
// =========================================================================
// TriggerAutoArchive manually triggers the cleanup procedure
// POST /api/admin/archive/trigger
func (c *AdminController) TriggerAutoArchive(ctx *gin.Context) {
	ipAddress := ctx.ClientIP()
	userAgent := ctx.Request.UserAgent()

	// Memanggil service yang menjalankan RAW SQL "CALL sp_archive_expired_items()"
	count, err := c.itemService.RunAutoArchive(ipAddress, userAgent)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to run archive procedure", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Auto-archive completed", gin.H{
		"archived_count": count,
		"method":         "Stored Procedure (Database)",
	})
}

// =========================================================================
// OPSI 2: ARSIP (Golang Logic - Begin/Commit)
// =========================================================================
// ArchiveExpired melakukan arsip menggunakan Begin/Commit/Rollback di Golang
// POST /api/admin/archive/manual-transaction
func (c *AdminController) ArchiveExpiredItemsManual(ctx *gin.Context) {
	// 1. BEGIN: Memulai Transaksi
	tx := c.DB.Begin()
	if tx.Error != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to start transaction", tx.Error.Error())
		return
	}

	// Safety: Defer Rollback (Jaga-jaga jika aplikasi crash/panic)
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	copyQuery := `
		INSERT INTO archives (item_id, name, description, category, found_location, found_date, image_url, finder_id)
		SELECT id, name, description, category, found_location, found_date, image_url, user_id
		FROM items 
		WHERE expires_at < NOW() AND status = 'unclaimed'
	`

	if err := tx.Exec(copyQuery).Error; err != nil {
		tx.Rollback() // ❌ ROLLBACK JIKA GAGAL COPY
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Transaction Failed: Could not copy to archives", err.Error())
		return
	}

	// 3. LANGKAH 2: Update status barang di tabel items
	updateQuery := `
		UPDATE items
		SET status = 'expired'
		WHERE expires_at < NOW() AND status = 'unclaimed'
	`

	result := tx.Exec(updateQuery)
	if result.Error != nil {
		tx.Rollback() // ❌ ROLLBACK JIKA GAGAL UPDATE (Data di archives juga akan batal masuk)
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Transaction Failed: Could not update item status", result.Error.Error())
		return
	}

	// Hitung berapa baris yang berubah
	itemsArchived := result.RowsAffected

	// 4. COMMIT: Simpan Perubahan Permanen
	if err := tx.Commit().Error; err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Transaction Failed: Could not commit changes", err.Error())
		return
	}

	// Sukses
	utils.SuccessResponse(ctx, http.StatusOK, "Manual Archive Transaction Successful", gin.H{
		"archived_count": itemsArchived,
		"method":         "Go Transaction (Begin/Commit/Rollback)",
	})
}

// GetFastDashboardStats uses Stored Procedure instead of View/Count
// GET /api/admin/dashboard/fast
func (c *AdminController) GetFastDashboardStats(ctx *gin.Context) {
	stats, err := c.dashboardService.GetStatsFromSP()
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get stats from SP", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Dashboard stats (SP) retrieved", stats)
}

// GetClaimsDetail - Get Claims with Details (PAKAI VIEW)
// GET /api/admin/claims-detail
func (c *AdminController) GetClaimsDetail(ctx *gin.Context) {
	status := ctx.Query("status")

	claims, err := c.dashboardService.GetClaimsWithDetails(status)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get claims detail", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Claims detail retrieved", claims)
}

// GetMatchesDetail - Get Match Results with Details (PAKAI VIEW)
// GET /api/admin/matches-detail
func (c *AdminController) GetMatchesDetail(ctx *gin.Context) {
	minScore, _ := strconv.ParseFloat(ctx.DefaultQuery("min_score", "0"), 64)

	matches, err := c.dashboardService.GetMatchesWithDetails(minScore)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get matches detail", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Matches detail retrieved", matches)
}

// GetUserActivity - Get User Activity (PAKAI VIEW)
// GET /api/admin/user-activity
func (c *AdminController) GetUserActivity(ctx *gin.Context) {
	limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "20"))

	activities, err := c.dashboardService.GetUserActivity(limit)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get user activity", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "User activity retrieved", activities)
}

// GetRecentActivities - Get Recent Activities (PAKAI VIEW vw_recent_activities)
// GET /api/admin/recent-activities
func (c *AdminController) GetRecentActivities(ctx *gin.Context) {
	activities, err := c.dashboardService.GetRecentActivities()
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get recent activities", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Recent activities retrieved", activities)
}