// internal/controllers/claim_controller.go
package controllers

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

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

type ClaimController struct {
	claimService        *services.ClaimService
	verificationService *services.VerificationService
}

func NewClaimController(db *gorm.DB) *ClaimController {
	return &ClaimController{
		claimService:        services.NewClaimService(db),
		verificationService: services.NewVerificationService(db),
	}
}

// GetAllClaims gets all claims
// GET /api/claims
func (c *ClaimController) GetAllClaims(ctx *gin.Context) {
	page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
	status := ctx.Query("status")

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

	// If regular user, only show their claims
	if userObj, exists := ctx.Get("user"); exists {
		user := userObj.(*models.User)
		if user.IsUser() {
			userID = &user.ID
		}
	}

	claims, total, err := c.claimService.GetAllClaims(page, limit, status, itemID, userID)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get claims", err.Error())
		return
	}

	// Pastikan claims selalu berupa array, bukan null
	if claims == nil {
		claims = []models.ClaimResponse{}
	}

	utils.SendPaginatedResponse(ctx, http.StatusOK, "Claims retrieved", claims, total, page, limit)
}

// POST /api/user/claims/:id/respond
func (c *ClaimController) UserApproveClaim(ctx *gin.Context) {
    userObj, exists := ctx.Get("user")
    if !exists {
        utils.ErrorResponse(ctx, http.StatusUnauthorized, "Unauthorized", "User not found")
        return
    }
    user := userObj.(*models.User)
    
    claimID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
    if err != nil {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid claim ID", err.Error())
        return
    }

    var req struct {
        Action string `json:"action" binding:"required"` // 'approve' or 'reject'
    }
    
    if err := ctx.ShouldBindJSON(&req); err != nil {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request body", err.Error())
        return
    }

    // Validasi Action
    if req.Action != "approve" && req.Action != "reject" {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid action", "Action must be 'approve' or 'reject'")
        return
    }

    // Logic approve/reject khusus user
    err = c.claimService.ProcessUserDecision(user.ID, uint(claimID), req.Action)
    if err != nil {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Gagal memproses keputusan", err.Error())
        return
    }

    message := "Klaim berhasil disetujui"
    if req.Action == "reject" {
        message = "Klaim berhasil ditolak"
    }

    utils.SuccessResponse(ctx, http.StatusOK, message, nil)
}

// GetClaimByID gets claim by ID
// GET /api/claims/:id
func (c *ClaimController) GetClaimByID(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid claim ID", err.Error())
		return
	}

	isManager := false
	if userObj, exists := ctx.Get("user"); exists {
		user := userObj.(*models.User)
		isManager = user.IsManager() || user.IsAdmin()
	}

	// Jika Manager, paksa hitung similarity dulu sebelum ambil data
	if isManager {
		_, err := c.verificationService.VerifyClaimDescription(uint(id))
		if err != nil {
			// Log error tapi jangan stop flow
		}
	}

	claim, err := c.claimService.GetClaimByID(uint(id), isManager)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusNotFound, "Claim not found", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Claim retrieved", claim)
}

func (c *ClaimController) UserConfirmCompletion(ctx *gin.Context) {
    userObj, _ := ctx.Get("user")
    user := userObj.(*models.User)
    
    claimID, _ := strconv.ParseUint(ctx.Param("id"), 10, 32)

    err := c.claimService.UserConfirmCompletion(user.ID, uint(claimID))
    if err != nil {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Gagal menyelesaikan kasus", err.Error())
        return
    }

    utils.SuccessResponse(ctx, http.StatusOK, "Kasus selesai. Barang telah diterima.", nil)
}

// CreateClaim creates a new claim
// POST /api/claims
func (c *ClaimController) CreateClaim(ctx *gin.Context) {
	userObj, _ := ctx.Get("user")
	user := userObj.(*models.User)

	var req services.CreateClaimRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
		return
	}

	ipAddress := ctx.ClientIP()
	userAgent := ctx.Request.UserAgent()

	claim, err := c.claimService.CreateClaim(user.ID, req, ipAddress, userAgent)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to create claim", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusCreated, "Claim created", claim.ToResponse())
}

// VerifyClaim verifies a claim (manager only)
// POST /api/claims/:id/verify
func (c *ClaimController) VerifyClaim(ctx *gin.Context) {
	managerObj, _ := ctx.Get("user")
	manager := managerObj.(*models.User)

	claimID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid claim ID", err.Error())
		return
	}

	var req services.VerifyClaimRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
		return
	}

	// Auto-verify description similarity
	verification, err := c.verificationService.VerifyClaimDescription(uint(claimID))
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Verification failed", err.Error())
		return
	}

	ipAddress := ctx.ClientIP()
	userAgent := ctx.Request.UserAgent()

	// Verify the claim
	if err := c.claimService.VerifyClaim(
		manager.ID,
		uint(claimID),
		req,
		verification.SimilarityScore,
		stringSliceToString(verification.MatchedKeywords),
		ipAddress,
		userAgent,
	); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to verify claim", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Claim verified", gin.H{
		"verification": verification,
	})
}

// GetClaimVerification gets verification data for a claim
// GET /api/claims/:id/verification
func (c *ClaimController) GetClaimVerification(ctx *gin.Context) {
	claimID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid claim ID", err.Error())
		return
	}

	verification, err := c.verificationService.VerifyClaimDescription(uint(claimID))
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Verification failed", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Verification retrieved", verification)
}

// CancelClaimApproval handles cancelling an approved claim
// POST /api/claims/:id/cancel-approval
// ✅ RENAMED: CancelApproval -> CancelClaimApproval (Matches routes.go)
func (c *ClaimController) CancelClaimApproval(ctx *gin.Context) {
	managerObj, _ := ctx.Get("user")
	manager := managerObj.(*models.User)

	claimID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid claim ID", err.Error())
		return
	}

	if err := c.claimService.CancelClaimApproval(manager.ID, uint(claimID)); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to cancel approval", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Approval cancelled successfully", nil)
}

// CloseCase handles case closure (Manager only)
// POST /api/claims/:id/close
// ✅ RENAMED: CloseClaim -> CloseCase (Matches routes.go)
func (c *ClaimController) CloseCase(ctx *gin.Context) {
	// 1. Ambil User (Manager) dari context secara konsisten
	managerObj, exists := ctx.Get("user")
	if !exists {
		utils.ErrorResponse(ctx, http.StatusUnauthorized, "Unauthorized", "User not found")
		return
	}
	manager := managerObj.(*models.User)

	// 2. Ambil ID Klaim dari URL
	idStr := ctx.Param("id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid ID format", err.Error())
		return
	}

	// 3. Parse Body Request
	var req services.CloseCaseRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
		return
	}

	// 4. Ambil Info Audit
	ipAddress := ctx.ClientIP()
	userAgent := ctx.Request.UserAgent()

	// 5. Panggil Service
	if err := c.claimService.CloseCase(manager.ID, uint(id), req, ipAddress, userAgent); err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to close case", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Case closed successfully", nil)
}

// ReopenCase handles reopening a closed case
// POST /api/claims/:id/reopen
// ✅ RENAMED: ReopenClaim -> ReopenCase (Matches routes.go)
func (c *ClaimController) ReopenCase(ctx *gin.Context) {
	managerObj, _ := ctx.Get("user")
	manager := managerObj.(*models.User)

	claimID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid claim ID", err.Error())
		return
	}

	var req services.ReopenCaseRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
		return
	}

	ipAddress := ctx.ClientIP()
	userAgent := ctx.Request.UserAgent()

	if err := c.claimService.ReopenCase(manager.ID, uint(claimID), req, ipAddress, userAgent); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to reopen case", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Case reopened successfully", nil)
}

// GetClaimsByUser gets claims by user
// GET /api/user/claims
func (c *ClaimController) GetClaimsByUser(ctx *gin.Context) {
	userObj, _ := ctx.Get("user")
	user := userObj.(*models.User)

	page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))

	claims, total, err := c.claimService.GetClaimsByUser(user.ID, page, limit)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get claims", err.Error())
		return
	}

	utils.SendPaginatedResponse(ctx, http.StatusOK, "Claims retrieved", claims, total, page, limit)
}

// UpdateClaim updates a claim
// PUT /api/claims/:id
func (c *ClaimController) UpdateClaim(ctx *gin.Context) {
	// Bisa Admin atau User pemilik klaim (dicek di middleware/service)
	userObj, _ := ctx.Get("user")
	user := userObj.(*models.User)

	claimID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid claim ID", err.Error())
		return
	}

	var req services.UpdateClaimRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
		return
	}

	ipAddress := ctx.ClientIP()
	userAgent := ctx.Request.UserAgent()

	claim, err := c.claimService.UpdateClaim(user.ID, uint(claimID), req, ipAddress, userAgent)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to update claim", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Claim updated successfully", claim)
}

// DeleteClaim deletes a claim
// DELETE /api/claims/:id
func (c *ClaimController) DeleteClaim(ctx *gin.Context) {
	userObj, _ := ctx.Get("user")
	user := userObj.(*models.User)

	claimID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid claim ID", err.Error())
		return
	}

	ipAddress := ctx.ClientIP()
	userAgent := ctx.Request.UserAgent()

	if err := c.claimService.DeleteClaim(user.ID, uint(claimID), ipAddress, userAgent); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to delete claim", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Claim deleted", nil)
}

// Helper function to convert string slice to string
func stringSliceToString(slice []string) string {
	if len(slice) == 0 {
		return ""
	}
	result := ""
	for i, s := range slice {
		if i > 0 {
			result += ", "
		}
		result += s
	}
	return result
}