package rest

import (
	"context"
	"errors"
	"fmt"
	"net/http"

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

// PermissionsHandler handles permission-related REST endpoints.
type PermissionsHandler struct {
	svcMgr *services.ServiceManager
}

// NewPermissionsHandler creates a new PermissionsHandler.
func NewPermissionsHandler(svcMgr *services.ServiceManager) *PermissionsHandler {
	return &PermissionsHandler{svcMgr: svcMgr}
}

// ListObjectPermissions handles GET /objectpermissions.
// Returns effective object permissions for the current user.
func (h *PermissionsHandler) ListObjectPermissions(c *gin.Context) {
	user := MustGetUser(c)

	perms, err := h.svcMgr.Permissions.ListEffectiveObjectPermissionsForUser(c.Request.Context(), user.ID, user.ProfileID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, apierr.SFErrorResponse("SERVER_ERROR", err.Error()))
		return
	}

	// Transform to Salesforce-compatible format
	records := make([]gin.H, len(perms))
	for i, p := range perms {
		records[i] = gin.H{
			"SobjectType":                 p.SobjectType,
			"PermissionsRead":             p.PermissionsRead,
			"PermissionsCreate":           p.PermissionsCreate,
			"PermissionsEdit":             p.PermissionsEdit,
			"PermissionsDelete":           p.PermissionsDelete,
			"PermissionsViewAllRecords":   p.PermissionsViewAllRecords,
			"PermissionsModifyAllRecords": p.PermissionsModifyAllRecords,
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"objectPermissions": records,
	})
}

// SystemPermissionCatalog handles GET /permissions/system/catalog.
// Returns all system permission definitions from the _System_Permission
// table so admin-authored rows and shipped platform rows surface the
// same way.
func (h *PermissionsHandler) SystemPermissionCatalog(c *gin.Context) {
	allPerms, err := h.svcMgr.Permissions.ListSystemPermissions(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, apierr.SFErrorResponse("SERVER_ERROR", err.Error()))
		return
	}
	records := make([]gin.H, len(allPerms))
	for i, p := range allPerms {
		desc := ""
		if p.Description != nil {
			desc = *p.Description
		}
		rec := gin.H{
			"apiName":     p.APIName,
			"label":       p.Label,
			"category":    p.Category,
			"description": desc,
		}
		if p.RequiresPermission != nil && *p.RequiresPermission != "" {
			rec["requires"] = *p.RequiresPermission
		}
		records[i] = rec
	}
	c.JSON(http.StatusOK, gin.H{"permissions": records})
}

// GetPermissionSetSystemGrants handles GET /permissions/system/permissionset/:id.
// Returns all system permission grants for a single PermissionSet from SSoT.
func (h *PermissionsHandler) GetPermissionSetSystemGrants(c *gin.Context) {
	permSetID, ok := RequireParam(c, "id")
	if !ok {
		return
	}

	grants, err := h.svcMgr.Permissions.GetPermissionSetGrants(c.Request.Context(), permSetID)
	if err != nil {
		var notFoundErr *appErrors.NotFoundError
		if errors.As(err, &notFoundErr) {
			c.JSON(http.StatusNotFound, apierr.SFErrorResponse("NOT_FOUND", notFoundErr.Error()))
			return
		}
		c.JSON(http.StatusInternalServerError, apierr.SFErrorResponse("SERVER_ERROR", err.Error()))
		return
	}

	c.JSON(http.StatusOK, gin.H{"grants": grants})
}

// SavePermissionSetSystemGrants handles PATCH /permissions/system/permissionset/:id.
// Accepts {"grants": {"ApiEnabled": true, "ViewAllData": false, ...}} and writes each to SSoT.
// Requires ManageProfilesPermSets permission (enforced at router level).
// All writes are wrapped in a single transaction; invalid permission keys are rejected.
func (h *PermissionsHandler) SavePermissionSetSystemGrants(c *gin.Context) {
	permSetID, ok := RequireParam(c, "id")
	if !ok {
		return
	}

	var body struct {
		Grants map[string]bool `json:"grants"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse("INVALID_REQUEST", "Invalid request body"))
		return
	}

	// Lock, validate, and write all inside one transaction.
	// The row lock serializes concurrent edits to the same PermissionSet.
	// Cache invalidation happens after commit, not inside the loop.
	// ApplyPermissionChanges validates catalog membership + prereqs,
	// writes diff-only junction rows, and records audit entries.
	var applyErr error
	var notFound bool
	err := h.svcMgr.TxManager.WithTransaction(c.Request.Context(), func(txCtx context.Context) error {
		exists, lockErr := h.svcMgr.Permissions.LockPermissionSet(txCtx, permSetID)
		if lockErr != nil {
			return lockErr
		}
		if !exists {
			notFound = true
			return fmt.Errorf("PermissionSet not found")
		}

		currentGrants, gErr := h.svcMgr.Permissions.GetPermissionSetGrants(txCtx, permSetID)
		if gErr != nil {
			return gErr
		}

		if aErr := h.svcMgr.Permissions.ApplyPermissionChanges(txCtx, permSetID, currentGrants, body.Grants, MustGetUser(c)); aErr != nil {
			applyErr = aErr
			return aErr
		}
		return nil
	})
	if err != nil {
		if notFound {
			c.JSON(http.StatusNotFound, apierr.SFErrorResponse("NOT_FOUND", "PermissionSet not found: "+permSetID))
			return
		}
		if applyErr != nil {
			// Unknown-permission and prereq errors are validation-class
			// (400). Catalog / junction failures come back as IsInternal
			// and must surface as 500 so operators see the real signal
			// (see backend/pkg/errors/errors.go:IsInternal).
			if appErrors.IsInternal(applyErr) {
				c.JSON(http.StatusInternalServerError, apierr.SFErrorResponse("SERVER_ERROR", applyErr.Error()))
				return
			}
			c.JSON(http.StatusBadRequest, apierr.SFErrorResponse("INVALID_FIELD", applyErr.Error()))
			return
		}
		c.JSON(http.StatusInternalServerError, apierr.SFErrorResponse("SERVER_ERROR", err.Error()))
		return
	}

	// Invalidate cache once, after TX commits successfully
	h.svcMgr.Permissions.InvalidateCache()

	c.JSON(http.StatusOK, gin.H{"success": true})
}

// GetEffectivePermissions handles GET /permissions/effective.
// Returns the current user's effective system + object permissions.
func (h *PermissionsHandler) GetEffectivePermissions(c *gin.Context) {
	user := MustGetUser(c)

	sysPerms, err := h.svcMgr.Permissions.GetEffectiveSystemPermissions(c.Request.Context(), user.ID, user.ProfileID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, apierr.SFErrorResponse("SERVER_ERROR", err.Error()))
		return
	}

	objPerms, err := h.svcMgr.Permissions.ListEffectiveObjectPermissionsForUser(c.Request.Context(), user.ID, user.ProfileID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, apierr.SFErrorResponse("SERVER_ERROR", err.Error()))
		return
	}

	objRecords := make([]gin.H, len(objPerms))
	for i, p := range objPerms {
		objRecords[i] = gin.H{
			"SobjectType":                 p.SobjectType,
			"PermissionsRead":             p.PermissionsRead,
			"PermissionsCreate":           p.PermissionsCreate,
			"PermissionsEdit":             p.PermissionsEdit,
			"PermissionsDelete":           p.PermissionsDelete,
			"PermissionsViewAllRecords":   p.PermissionsViewAllRecords,
			"PermissionsModifyAllRecords": p.PermissionsModifyAllRecords,
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"systemPermissions": sysPerms,
		"objectPermissions": objRecords,
	})
}
