package rest

import (
	"errors"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/internal/application/services"
	"github.com/nexuscrm/backend/pkg/apierr"
	"github.com/nexuscrm/shared/pkg/constants"
)

type RecycleBinHandler struct {
	svcMgr *services.ServiceManager
}

func NewRecycleBinHandler(svcMgr *services.ServiceManager) *RecycleBinHandler {
	return &RecycleBinHandler{svcMgr: svcMgr}
}

func (h *RecycleBinHandler) List(c *gin.Context) {
	user := MustGetUser(c)
	scope := normalizeRecycleBinScope(c.Query("scope"))
	allowOrgScope := h.canViewOrgRecycleBin(c)
	canMutateOrgScope := h.canMutateOrgRecycleBin(c)
	if scope == "all" && !allowOrgScope {
		c.JSON(http.StatusForbidden, apierr.SFErrorResponse(apierr.SFCodeForbidden, "Org recycle bin requires ViewAllData or ModifyAllData"))
		return
	}

	records, err := h.svcMgr.Persistence.ListRecycleBinItems(c.Request.Context(), user, scope == "all")
	if err != nil {
		WriteError(c, err, apierr.SFCodeUnknownException)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"scope":             scope,
		"records":           records,
		"canViewOrgScope":   allowOrgScope,
		"canMutateOrgScope": canMutateOrgScope,
	})
}

func (h *RecycleBinHandler) Restore(c *gin.Context) {
	user := MustGetUser(c)
	recordID, ok := RequireParam(c, "id")
	if !ok {
		return
	}
	if err := h.svcMgr.Persistence.RestoreRecycleBinRecord(c.Request.Context(), recordID, user, h.canMutateOrgRecycleBin(c)); err != nil {
		h.writeError(c, err)
		return
	}
	c.Status(http.StatusNoContent)
	c.Writer.WriteHeaderNow()
}

func (h *RecycleBinHandler) Purge(c *gin.Context) {
	user := MustGetUser(c)
	recordID, ok := RequireParam(c, "id")
	if !ok {
		return
	}
	if err := h.svcMgr.Persistence.PurgeRecycleBinRecord(c.Request.Context(), recordID, user, h.canMutateOrgRecycleBin(c)); err != nil {
		h.writeError(c, err)
		return
	}
	c.Status(http.StatusNoContent)
	c.Writer.WriteHeaderNow()
}

func (h *RecycleBinHandler) canViewOrgRecycleBin(c *gin.Context) bool {
	if perms, ok := c.Get(constants.ContextKeySystemPermissions); ok {
		if systemPerms, ok := perms.(map[string]bool); ok {
			viewAll, hasViewAll := systemPerms[string(constants.PermViewAllData)]
			modifyAll, hasModifyAll := systemPerms[string(constants.PermModifyAllData)]
			if hasViewAll || hasModifyAll {
				return viewAll || modifyAll
			}
		}
	}

	user := MustGetUser(c)
	return h.svcMgr != nil &&
		h.svcMgr.Permissions != nil &&
		(h.svcMgr.Permissions.CheckSystemPermission(c.Request.Context(), user, string(constants.PermViewAllData)) ||
			h.svcMgr.Permissions.CheckSystemPermission(c.Request.Context(), user, string(constants.PermModifyAllData)))
}

func (h *RecycleBinHandler) canMutateOrgRecycleBin(c *gin.Context) bool {
	if perms, ok := c.Get(constants.ContextKeySystemPermissions); ok {
		if systemPerms, ok := perms.(map[string]bool); ok {
			modifyAll, hasModifyAll := systemPerms[string(constants.PermModifyAllData)]
			if hasModifyAll {
				return modifyAll
			}
		}
	}

	user := MustGetUser(c)
	return h.svcMgr != nil &&
		h.svcMgr.Permissions != nil &&
		h.svcMgr.Permissions.CheckSystemPermission(c.Request.Context(), user, string(constants.PermModifyAllData))
}

func normalizeRecycleBinScope(raw string) string {
	if strings.EqualFold(raw, "all") {
		return "all"
	}
	return "mine"
}

func (h *RecycleBinHandler) writeError(c *gin.Context, err error) {
	if err == nil {
		return
	}
	if RespondError(c, err) {
		return
	}
	var apiErr *apierr.Error
	if errors.As(err, &apiErr) {
		c.JSON(apiErr.Status, apierr.SFErrorResponse(apierr.SFCodeUnknownException, apiErr.Error()))
		return
	}
	WriteError(c, err, apierr.SFCodeUnknownException)
}
