package handlers

import (
	"database/sql"
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"

	"switchboard-core/models"
	"switchboard-core/store"
	"switchboard-core/workflow"
)

// ── Assignment Handlers ─────────────────────

// WorkflowAssignmentHandler manages workflow assignment HTTP endpoints.
type WorkflowAssignmentHandler struct {
	engine *workflow.Engine
	stores store.Stores
}

// NewWorkflowAssignmentHandler creates an assignment handler.
func NewWorkflowAssignmentHandler(engine *workflow.Engine, stores store.Stores) *WorkflowAssignmentHandler {
	return &WorkflowAssignmentHandler{engine: engine, stores: stores}
}

// Claim claims an assignment for the current user.
// POST /api/v1/assignments/:id/claim
func (h *WorkflowAssignmentHandler) Claim(c *gin.Context) {
	userID := c.GetString("user_id")
	assignmentID := c.Param("id")
	ctx := c.Request.Context()

	// Claim first, then verify role. Rollback if role check fails.
	if err := h.stores.Workflows.ClaimAssignment(ctx, assignmentID, userID); err != nil {
		c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
		return
	}

	// Role check: find the assignment in user's claimed list and verify role
	claimed, _ := h.stores.Workflows.ListAssignmentsByUser(ctx, userID, models.AssignmentStatusClaimed)
	for _, a := range claimed {
		if a.ID == assignmentID {
			if rerr := workflow.CheckClaimRole(ctx, h.stores, &a, userID); rerr != nil {
				// Rollback: unclaim
				h.stores.Workflows.UnclaimAssignment(ctx, assignmentID)
				c.JSON(http.StatusForbidden, gin.H{"error": rerr.Error()})
				return
			}
			break
		}
	}

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

// Unclaim releases a claimed assignment back to the queue.
// POST /api/v1/assignments/:id/unclaim
func (h *WorkflowAssignmentHandler) Unclaim(c *gin.Context) {
	userID := c.GetString("user_id")
	assignmentID := c.Param("id")

	// Verify current user is the claimer
	userAssignments, err := h.stores.Workflows.ListAssignmentsByUser(c.Request.Context(), userID, models.AssignmentStatusClaimed)
	if err == nil {
		found := false
		for _, a := range userAssignments {
			if a.ID == assignmentID {
				found = true
				break
			}
		}
		if !found {
			c.JSON(http.StatusForbidden, gin.H{"error": "assignment not claimed by you"})
			return
		}
	}

	if err := h.stores.Workflows.UnclaimAssignment(c.Request.Context(), assignmentID); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

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

// Complete marks a claimed assignment as completed.
// POST /api/v1/assignments/:id/complete
func (h *WorkflowAssignmentHandler) Complete(c *gin.Context) {
	userID := c.GetString("user_id")
	assignmentID := c.Param("id")

	var body struct {
		ReviewData json.RawMessage `json:"review_data"`
	}
	if err := c.ShouldBindJSON(&body); err != nil && err.Error() != "EOF" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}

	// Verify current user is the claimer
	userAssignments, err := h.stores.Workflows.ListAssignmentsByUser(c.Request.Context(), userID, models.AssignmentStatusClaimed)
	if err == nil {
		found := false
		for _, a := range userAssignments {
			if a.ID == assignmentID {
				found = true
				break
			}
		}
		if !found {
			c.JSON(http.StatusForbidden, gin.H{"error": "assignment not claimed by you"})
			return
		}
	}

	if err := h.stores.Workflows.CompleteAssignment(c.Request.Context(), assignmentID, body.ReviewData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// If the stage has auto_transition, advance the instance.
	// Look up the assignment's instance to get context.
	userAssignments, _ = h.stores.Workflows.ListAssignmentsByUser(c.Request.Context(), userID, models.AssignmentStatusCompleted)
	for _, a := range userAssignments {
		if a.ID == assignmentID {
			// Try to advance — engine will handle validation
			h.engine.Advance(c.Request.Context(), a.InstanceID, body.ReviewData, userID)
			break
		}
	}

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

// Cancel cancels an open assignment.
// POST /api/v1/assignments/:id/cancel
func (h *WorkflowAssignmentHandler) Cancel(c *gin.Context) {
	assignmentID := c.Param("id")

	if err := h.stores.Workflows.CancelAssignment(c.Request.Context(), assignmentID); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

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

// ListByTeam returns assignments for a team.
// GET /api/v1/teams/:teamId/assignments?status=
func (h *WorkflowAssignmentHandler) ListByTeam(c *gin.Context) {
	teamID := c.Param("teamId")
	status := c.Query("status")

	result, err := h.stores.Workflows.ListAssignmentsByTeam(c.Request.Context(), teamID, status)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusOK, gin.H{"data": []struct{}{}})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list assignments"})
		return
	}
	if result == nil {
		c.JSON(http.StatusOK, gin.H{"data": []struct{}{}})
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": result})
}

// ListMine returns assignments claimed by or assigned to the current user.
// GET /api/v1/assignments/mine?status=
func (h *WorkflowAssignmentHandler) ListMine(c *gin.Context) {
	userID := c.GetString("user_id")
	status := c.Query("status")

	result, err := h.stores.Workflows.ListAssignmentsByUser(c.Request.Context(), userID, status)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list assignments"})
		return
	}
	if result == nil {
		c.JSON(http.StatusOK, gin.H{"data": []struct{}{}})
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": result})
}
