package rest

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/internal/features/permissions"
	"github.com/nexuscrm/backend/pkg/apierr"
)

// FolderShareResponse represents a folder share in Salesforce wire format
type FolderShareResponse struct {
	ID           string `json:"id"`
	SharedToID   string `json:"sharedToId"`
	SharedToType string `json:"sharedToType"` // User, Group, Role
	AccessLevel  string `json:"accessLevel"`  // View, Manage
}

func folderShareRowToResponse(s *permissions.FolderShareRow) FolderShareResponse {
	return FolderShareResponse{
		ID:           s.ID,
		SharedToID:   s.SharedToID,
		SharedToType: s.SharedToType,
		AccessLevel:  s.AccessLevel,
	}
}

// AddFolderSharesRequest represents a request to add shares (append)
type AddFolderSharesRequest struct {
	Shares []struct {
		SharedToID   string `json:"sharedToId" binding:"required"`
		SharedToType string `json:"sharedToType"` // User, Group, Role
		AccessLevel  string `json:"accessLevel"`  // View, Manage
	} `json:"shares" binding:"required"`
}

// UpdateFolderSharesRequest represents a folder shares update request
type UpdateFolderSharesRequest struct {
	Shares []struct {
		SharedToID   string `json:"sharedToId" binding:"required"`
		SharedToType string `json:"sharedToType"` // User, Group, Role
		AccessLevel  string `json:"accessLevel"`  // View, Manage
	} `json:"shares" binding:"required"`
}

// UpdateFolderShareRequest represents a request to update a share's access level
type UpdateFolderShareRequest struct {
	AccessLevel string `json:"accessLevel" binding:"required"`
}

// GetFolderShares handles GET /folders/:id/shares
func (h *FolderHandler) GetFolderShares(c *gin.Context) {
	user := MustGetUser(c)

	folderID, ok := RequireParam(c, "id")
	if !ok {
		return
	}

	if !validateID(folderID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid folder ID format"))
		return
	}

	ctx := c.Request.Context()

	if h.folderAccess != nil && !h.folderAccess.CheckAccess(ctx, user, folderID, permissions.FolderAccessView) {
		c.JSON(http.StatusNotFound, gin.H{"errorCode": "NOT_FOUND", "message": fmt.Sprintf("Folder %s not found or access denied", folderID)})
		return
	}

	// Check folder exists
	if !h.folderExists(ctx, folderID, user) {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Folder not found"))
		return
	}

	rows, err := h.folderAccess.ListShares(ctx, folderID)
	if err != nil {
		RespondAppError(c, err)
		return
	}

	shares := make([]FolderShareResponse, 0, len(rows))
	for i := range rows {
		shares = append(shares, folderShareRowToResponse(&rows[i]))
	}

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

// AddFolderShares handles POST /folders/:id/shares - adds shares (appends to existing)
func (h *FolderHandler) AddFolderShares(c *gin.Context) {
	user := MustGetUser(c)

	folderID, ok := RequireParam(c, "id")
	if !ok {
		return
	}

	if !validateID(folderID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid folder ID format"))
		return
	}

	var req AddFolderSharesRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidRequest, err.Error()))
		return
	}

	ctx := c.Request.Context()

	if h.folderAccess != nil && !h.folderAccess.CheckAccess(ctx, user, folderID, permissions.FolderAccessManage) {
		c.JSON(http.StatusNotFound, gin.H{"errorCode": "NOT_FOUND", "message": fmt.Sprintf("Folder %s not found or access denied", folderID)})
		return
	}
	if h.folderAccess != nil && !checkFolderTypePermission(c, h.folderAccess.GetFolderType(ctx, folderID)) {
		c.JSON(http.StatusForbidden, gin.H{"errorCode": "FORBIDDEN", "message": "You do not have permission to manage this folder type"})
		return
	}

	// Check folder exists
	if !h.folderExists(ctx, folderID, user) {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Folder not found"))
		return
	}

	// Add new shares (append to existing)
	shares := make([]FolderShareResponse, 0, len(req.Shares))
	for _, s := range req.Shares {
		// Validate SharedToId — Organization shares use sentinel "ALL_INTERNAL", skip UUID validation
		sharedToType := normalizeSharedToType(s.SharedToType)
		if sharedToType != "Organization" && !validateID(s.SharedToID) {
			c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid sharedToId format"))
			return
		}

		id, err := h.folderAccess.InsertShare(ctx, folderID, s.SharedToID, sharedToType, normalizeAccessLevel(s.AccessLevel), user.ID)
		if err != nil {
			RespondAppError(c, err)
			return
		}
		shares = append(shares, FolderShareResponse{
			ID:           id,
			SharedToID:   s.SharedToID,
			SharedToType: sharedToType,
			AccessLevel:  normalizeAccessLevel(s.AccessLevel),
		})
	}

	c.JSON(http.StatusCreated, gin.H{"shares": shares})
}

// UpdateFolderShares handles PUT /folders/:id/shares
func (h *FolderHandler) UpdateFolderShares(c *gin.Context) {
	user := MustGetUser(c)

	folderID, ok := RequireParam(c, "id")
	if !ok {
		return
	}

	if !validateID(folderID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid folder ID format"))
		return
	}

	var req UpdateFolderSharesRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidRequest, err.Error()))
		return
	}

	ctx := c.Request.Context()

	if h.folderAccess != nil && !h.folderAccess.CheckAccess(ctx, user, folderID, permissions.FolderAccessManage) {
		c.JSON(http.StatusNotFound, gin.H{"errorCode": "NOT_FOUND", "message": fmt.Sprintf("Folder %s not found or access denied", folderID)})
		return
	}
	if h.folderAccess != nil && !checkFolderTypePermission(c, h.folderAccess.GetFolderType(ctx, folderID)) {
		c.JSON(http.StatusForbidden, gin.H{"errorCode": "FORBIDDEN", "message": "You do not have permission to manage this folder type"})
		return
	}

	// Check folder exists
	if !h.folderExists(ctx, folderID, user) {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Folder not found"))
		return
	}

	// Validate all inputs before the atomic replace
	inputs := make([]permissions.ShareInput, 0, len(req.Shares))
	for _, s := range req.Shares {
		sharedToType := normalizeSharedToType(s.SharedToType)
		if sharedToType != "Organization" && !validateID(s.SharedToID) {
			c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid sharedToId format"))
			return
		}
		inputs = append(inputs, permissions.ShareInput{
			SharedToID:   s.SharedToID,
			SharedToType: sharedToType,
			AccessLevel:  normalizeAccessLevel(s.AccessLevel),
		})
	}

	// Atomic replace: delete all + insert new in one transaction
	ids, err := h.folderAccess.ReplaceAllShares(ctx, folderID, user.ID, inputs)
	if err != nil {
		RespondAppError(c, err)
		return
	}

	shares := make([]FolderShareResponse, 0, len(inputs))
	for i, s := range inputs {
		shares = append(shares, FolderShareResponse{
			ID:           ids[i],
			SharedToID:   s.SharedToID,
			SharedToType: s.SharedToType,
			AccessLevel:  s.AccessLevel,
		})
	}

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

// GetFolderShare handles GET /folders/:id/shares/:shareId
func (h *FolderHandler) GetFolderShare(c *gin.Context) {
	user := MustGetUser(c)

	folderID := c.Param("id")
	shareID := c.Param("shareId")

	if !validateID(folderID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid folder ID format"))
		return
	}
	if !validateID(shareID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid share ID format"))
		return
	}

	ctx := c.Request.Context()

	if h.folderAccess != nil && !h.folderAccess.CheckAccess(ctx, user, folderID, permissions.FolderAccessView) {
		c.JSON(http.StatusNotFound, gin.H{"errorCode": "NOT_FOUND", "message": fmt.Sprintf("Folder %s not found or access denied", folderID)})
		return
	}

	// Check folder exists
	if !h.folderExists(ctx, folderID, user) {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Folder not found"))
		return
	}

	// Get the share
	share, err := h.folderAccess.GetShare(ctx, shareID)
	if err != nil {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Share not found"))
		return
	}

	// Verify share belongs to this folder
	if share.FolderID != folderID {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Share not found"))
		return
	}

	c.JSON(http.StatusOK, folderShareRowToResponse(share))
}

// UpdateFolderShare handles PATCH /folders/:id/shares/:shareId
func (h *FolderHandler) UpdateFolderShare(c *gin.Context) {
	user := MustGetUser(c)

	folderID := c.Param("id")
	shareID := c.Param("shareId")

	if !validateID(folderID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid folder ID format"))
		return
	}
	if !validateID(shareID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid share ID format"))
		return
	}

	var req UpdateFolderShareRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidRequest, err.Error()))
		return
	}

	ctx := c.Request.Context()

	if h.folderAccess != nil && !h.folderAccess.CheckAccess(ctx, user, folderID, permissions.FolderAccessManage) {
		c.JSON(http.StatusNotFound, gin.H{"errorCode": "NOT_FOUND", "message": fmt.Sprintf("Folder %s not found or access denied", folderID)})
		return
	}
	if h.folderAccess != nil && !checkFolderTypePermission(c, h.folderAccess.GetFolderType(ctx, folderID)) {
		c.JSON(http.StatusForbidden, gin.H{"errorCode": "FORBIDDEN", "message": "You do not have permission to manage this folder type"})
		return
	}

	// Check folder exists
	if !h.folderExists(ctx, folderID, user) {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Folder not found"))
		return
	}

	// Get the share to verify it exists and belongs to this folder
	share, err := h.folderAccess.GetShare(ctx, shareID)
	if err != nil {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Share not found"))
		return
	}

	// Verify share belongs to this folder
	if share.FolderID != folderID {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Share not found"))
		return
	}

	// Update access level
	accessLevel := normalizeAccessLevel(req.AccessLevel)
	if err := h.folderAccess.UpdateShareAccessLevel(ctx, shareID, accessLevel); err != nil {
		RespondAppError(c, err)
		return
	}

	// Return the updated share
	share.AccessLevel = accessLevel
	c.JSON(http.StatusOK, folderShareRowToResponse(share))
}

// DeleteFolderShare handles DELETE /folders/:id/shares/:shareId
func (h *FolderHandler) DeleteFolderShare(c *gin.Context) {
	user := MustGetUser(c)

	folderID := c.Param("id")
	shareID := c.Param("shareId")

	if !validateID(folderID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid folder ID format"))
		return
	}
	if !validateID(shareID) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidField, "Invalid share ID format"))
		return
	}

	ctx := c.Request.Context()

	if h.folderAccess != nil && !h.folderAccess.CheckAccess(ctx, user, folderID, permissions.FolderAccessManage) {
		c.JSON(http.StatusNotFound, gin.H{"errorCode": "NOT_FOUND", "message": fmt.Sprintf("Folder %s not found or access denied", folderID)})
		return
	}
	if h.folderAccess != nil && !checkFolderTypePermission(c, h.folderAccess.GetFolderType(ctx, folderID)) {
		c.JSON(http.StatusForbidden, gin.H{"errorCode": "FORBIDDEN", "message": "You do not have permission to manage this folder type"})
		return
	}

	// Check folder exists
	if !h.folderExists(ctx, folderID, user) {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Folder not found"))
		return
	}

	// Get the share to verify it exists and belongs to this folder
	share, err := h.folderAccess.GetShare(ctx, shareID)
	if err != nil {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Share not found"))
		return
	}

	// Verify share belongs to this folder
	if share.FolderID != folderID {
		c.JSON(http.StatusNotFound, apierr.SFErrorResponse(apierr.SFCodeNotFound, "Share not found"))
		return
	}

	// Delete the share
	if err := h.folderAccess.DeleteShare(ctx, shareID); err != nil {
		RespondAppError(c, err)
		return
	}

	c.Status(http.StatusNoContent)
}
