// internal/controllers/lost_item_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 LostItemController struct {
	lostItemService *services.LostItemService
}

func NewLostItemController(db *gorm.DB) *LostItemController {
	return &LostItemController{
		lostItemService: services.NewLostItemService(db),
	}
}

// GetAllLostItems gets all lost items
// GET /api/lost-items
func (c *LostItemController) GetAllLostItems(ctx *gin.Context) {
    page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
    limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
    status := ctx.Query("status")
    category := ctx.Query("category")
    search := ctx.Query("search")
    scope := ctx.Query("scope") // Tambahkan ini

    var userID *uint

    // Logic Baru: Defaultnya publik (userID = nil), kecuali minta 'mine'
    if userObj, exists := ctx.Get("user"); exists {
        user := userObj.(*models.User)
        
        // Hanya filter ID jika user meminta scope="mine"
        if scope == "mine" {
            userID = &user.ID
        }
    }

    // Panggil service (userID akan nil jika melihat publik, terisi jika scope=mine)
    lostItems, total, err := c.lostItemService.GetAllLostItems(page, limit, status, category, search, userID)
    if err != nil {
        utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get lost items", err.Error())
        return
    }

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

// GetLostItemByID gets lost item by ID
// GET /api/lost-items/:id
func (c *LostItemController) GetLostItemByID(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid lost item ID", err.Error())
		return
	}

	lostItem, err := c.lostItemService.GetLostItemByID(uint(id))
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusNotFound, "Lost item not found", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Lost item retrieved", lostItem.ToResponse())
}

// CreateLostItem creates a new lost item report
// POST /api/lost-items
func (c *LostItemController) CreateLostItem(ctx *gin.Context) {
	userObj, _ := ctx.Get("user")
	user := userObj.(*models.User)

	var req services.CreateLostItemRequest
	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()

	lostItem, err := c.lostItemService.CreateLostItem(user.ID, req, ipAddress, userAgent)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to create lost item report", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusCreated, "Lost item report created", lostItem.ToResponse())
}

// UpdateLostItem updates a lost item report
// PUT /api/lost-items/:id
func (c *LostItemController) UpdateLostItem(ctx *gin.Context) {
	userObj, _ := ctx.Get("user")
	user := userObj.(*models.User)

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

	var req services.UpdateLostItemRequest
	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()

	lostItem, err := c.lostItemService.UpdateLostItem(user.ID, uint(lostItemID), req, ipAddress, userAgent)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to update lost item report", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Lost item report updated", lostItem.ToResponse())
}

// UpdateLostItemStatus updates lost item status
// PATCH /api/lost-items/:id/status
func (c *LostItemController) UpdateLostItemStatus(ctx *gin.Context) {
	userObj, _ := ctx.Get("user")
	user := userObj.(*models.User)

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

	var req struct {
		Status string `json:"status" binding:"required"`
	}
	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.lostItemService.UpdateLostItemStatus(user.ID, uint(lostItemID), req.Status, ipAddress, userAgent); err != nil {
		utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to update status", err.Error())
		return
	}

	utils.SuccessResponse(ctx, http.StatusOK, "Lost item status updated", nil)
}

// DeleteLostItem deletes a lost item report
// DELETE /api/lost-items/:id
func (c *LostItemController) DeleteLostItem(ctx *gin.Context) {
	userObj, _ := ctx.Get("user")
    user := userObj.(*models.User)

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

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

	if err := c.lostItemService.DeleteLostItem(user.ID, uint(lostItemID), ipAddress, userAgent);
    err != nil {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to delete lost item report", err.Error())
        return
    }

	utils.SuccessResponse(ctx, http.StatusOK, "Lost item report deleted", nil)
}

// GetLostItemsByUser gets lost items by user
// GET /api/user/lost-items
func (c *LostItemController) GetLostItemsByUser(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"))

	lostItems, total, err := c.lostItemService.GetLostItemsByUser(user.ID, page, limit)
	if err != nil {
		utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get lost items", err.Error())
		return
	}

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

func (c *LostItemController) DirectClaimToOwner(ctx *gin.Context) {
    userObj, _ := ctx.Get("user")
    user := userObj.(*models.User)
    
    lostItemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
    if err != nil {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid lost item ID", err.Error())
        return
    }
    
    var req services.CreateLostItemClaimRequest
    if err := ctx.ShouldBindJSON(&req); err != nil {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
        return
    }

    // ✅ TAMBAHAN BARU: Ambil IP dan User Agent untuk Audit Log
    ipAddress := ctx.ClientIP()
    userAgent := ctx.Request.UserAgent()
    
    // ✅ UPDATE: Kirim ipAddress dan userAgent ke Service
    claim, err := c.lostItemService.DirectClaimToOwner(user.ID, uint(lostItemID), req, ipAddress, userAgent)
    if err != nil {
        utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to submit direct claim", err.Error())
        return
    }
    
    utils.SuccessResponse(ctx, http.StatusCreated, "Klaim terkirim ke pemilik untuk persetujuan", claim.ToResponse())
}