package handlers

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

	"github.com/gin-gonic/gin"

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

// ── Instance Handlers ───────────────────────

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

// NewWorkflowInstanceHandler creates an instance handler.
func NewWorkflowInstanceHandler(engine *workflow.Engine, stores store.Stores) *WorkflowInstanceHandler {
	return &WorkflowInstanceHandler{engine: engine, stores: stores}
}

// Start creates a new workflow instance.
// POST /api/v1/workflows/:id/instances
func (h *WorkflowInstanceHandler) Start(c *gin.Context) {
	workflowID := c.Param("id")
	userID := c.GetString("user_id")

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

	inst, err := h.engine.Start(c.Request.Context(), workflowID, body.Data, userID)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, inst)
}

// ListInstances returns instances for a workflow.
// GET /api/v1/workflows/:id/instances?status=&limit=&offset=
func (h *WorkflowInstanceHandler) ListInstances(c *gin.Context) {
	workflowID := c.Param("id")
	status := c.Query("status")

	opts := store.ListOptions{}
	if l := c.Query("limit"); l != "" {
		opts.Limit, _ = strconv.Atoi(l)
	}
	if o := c.Query("offset"); o != "" {
		opts.Offset, _ = strconv.Atoi(o)
	}

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

// GetInstance returns a single instance.
// GET /api/v1/workflows/:id/instances/:iid
func (h *WorkflowInstanceHandler) GetInstance(c *gin.Context) {
	inst, err := h.stores.Workflows.GetInstance(c.Request.Context(), c.Param("iid"))
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "instance not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	// Verify it belongs to the requested workflow
	if inst.WorkflowID != c.Param("id") {
		c.JSON(http.StatusNotFound, gin.H{"error": "instance not found"})
		return
	}
	c.JSON(http.StatusOK, inst)
}

// Advance moves an instance to the next stage.
// POST /api/v1/workflows/:id/instances/:iid/advance
func (h *WorkflowInstanceHandler) Advance(c *gin.Context) {
	userID := c.GetString("user_id")

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

	inst, err := h.engine.Advance(c.Request.Context(), c.Param("iid"), body.Data, userID)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, inst)
}

// Cancel terminates an active instance.
// POST /api/v1/workflows/:id/instances/:iid/cancel
func (h *WorkflowInstanceHandler) Cancel(c *gin.Context) {
	userID := c.GetString("user_id")

	if err := h.engine.Cancel(c.Request.Context(), c.Param("iid"), userID); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

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