package handlers

// workflow_monitor.go — v0.35.0 Monitoring Dashboard
//
// Provides admin and team-scoped views of active workflow instances,
// stage funnels, and stale instance detection for SLA tracking.

import (
	"context"
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"

	"chat-switchboard/store"
)

// ── Workflow Monitor Handler ─────────────────

type WorkflowMonitorHandler struct {
	stores store.Stores
}

func NewWorkflowMonitorHandler(stores store.Stores) *WorkflowMonitorHandler {
	return &WorkflowMonitorHandler{stores: stores}
}

// ── Instance types ──────────────────────────

// MonitorInstance is a single active workflow instance for the monitoring dashboard.
type MonitorInstance struct {
	ChannelID    string  `json:"channel_id"`
	ChannelTitle string  `json:"channel_title"`
	WorkflowID   string  `json:"workflow_id"`
	WorkflowName string  `json:"workflow_name"`
	CurrentStage int     `json:"current_stage"`
	StageName    string  `json:"stage_name"`
	AssignedTo   *string `json:"assigned_to,omitempty"`
	AgeSeconds   int64   `json:"age_seconds"`
	StageAgeSeconds int64 `json:"stage_age_seconds"`
	SLASeconds     *int  `json:"sla_seconds,omitempty"`
	SLARemaining   *int64 `json:"sla_remaining_seconds,omitempty"`
	SLABreached    bool   `json:"sla_breached"`
	LastActivityAt string `json:"last_activity_at"`
}

// FunnelEntry is a count of instances at a given stage.
type FunnelEntry struct {
	StageOrdinal int    `json:"stage_ordinal"`
	StageName    string `json:"stage_name"`
	Count        int    `json:"count"`
}

// ── Endpoints ───────────────────────────────

// ListActiveInstances returns all active workflow instances.
// GET /api/v1/admin/workflows/monitor/instances
func (h *WorkflowMonitorHandler) ListActiveInstances(c *gin.Context) {
	ctx := c.Request.Context()

	instances, err := h.queryActiveInstances(ctx, "")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query instances: " + err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": instances})
}

// ListTeamActiveInstances returns active workflow instances for a team.
// GET /api/v1/teams/:teamId/workflows/monitor/instances
func (h *WorkflowMonitorHandler) ListTeamActiveInstances(c *gin.Context) {
	teamID := c.Param("teamId")
	ctx := c.Request.Context()

	instances, err := h.queryActiveInstances(ctx, teamID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query instances: " + err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": instances})
}

// GetFunnel returns stage-by-stage instance counts for a workflow.
// GET /api/v1/admin/workflows/monitor/funnel/:id
func (h *WorkflowMonitorHandler) GetFunnel(c *gin.Context) {
	workflowID := c.Param("id")
	ctx := c.Request.Context()

	stages, err := h.stores.Workflows.ListStages(ctx, workflowID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load stages"})
		return
	}

	instances, err := h.queryActiveInstances(ctx, "")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query instances"})
		return
	}

	// Count instances at each stage
	counts := make(map[int]int)
	for _, inst := range instances {
		if inst.WorkflowID == workflowID {
			counts[inst.CurrentStage]++
		}
	}

	funnel := make([]FunnelEntry, len(stages))
	for i, s := range stages {
		funnel[i] = FunnelEntry{
			StageOrdinal: s.Ordinal,
			StageName:    s.Name,
			Count:        counts[s.Ordinal],
		}
	}

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

// ListStaleInstances returns workflow instances that haven't been touched recently.
// GET /api/v1/admin/workflows/monitor/stale
func (h *WorkflowMonitorHandler) ListStaleInstances(c *gin.Context) {
	thresholdHours, _ := strconv.Atoi(c.DefaultQuery("threshold_hours", "48"))
	if thresholdHours <= 0 {
		thresholdHours = 48
	}

	ctx := c.Request.Context()
	instances, err := h.queryActiveInstances(ctx, "")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query instances"})
		return
	}

	now := time.Now().UTC()
	threshold := time.Duration(thresholdHours) * time.Hour
	var stale []MonitorInstance

	for _, inst := range instances {
		if inst.AgeSeconds > 0 && time.Duration(inst.AgeSeconds)*time.Second > threshold {
			stale = append(stale, inst)
		} else {
			// Also check last_activity_at
			if t, err := time.Parse(time.RFC3339, inst.LastActivityAt); err == nil {
				if now.Sub(t) > threshold {
					stale = append(stale, inst)
				}
			}
		}
	}

	if stale == nil {
		stale = []MonitorInstance{}
	}
	c.JSON(http.StatusOK, gin.H{"data": stale, "threshold_hours": thresholdHours})
}

// ── Helpers ─────────────────────────────────

func (h *WorkflowMonitorHandler) queryActiveInstances(ctx context.Context, teamIDFilter string) ([]MonitorInstance, error) {
	// Query all active workflow channels via store
	// This is a new query we build from existing store methods
	channels, err := h.stores.Channels.ListByType(ctx, "workflow")
	if err != nil {
		return nil, err
	}

	now := time.Now().UTC()
	var result []MonitorInstance

	for _, ch := range channels {
		ws, err := h.stores.Channels.GetWorkflowStatus(ctx, ch.ID)
		if err != nil || ws == nil || ws.WorkflowID == nil || ws.Status != "active" {
			continue
		}

		// Team filter
		if teamIDFilter != "" {
			if ch.TeamID == nil || *ch.TeamID != teamIDFilter {
				continue
			}
		}

		wf, err := h.stores.Workflows.GetByID(ctx, *ws.WorkflowID)
		if err != nil || wf == nil {
			continue
		}

		stages, _ := h.stores.Workflows.ListStages(ctx, *ws.WorkflowID)
		var stageName string
		var slaSeconds *int
		if ws.CurrentStage < len(stages) {
			stageName = stages[ws.CurrentStage].Name
			slaSeconds = stages[ws.CurrentStage].SLASeconds
		}

		inst := MonitorInstance{
			ChannelID:      ch.ID,
			ChannelTitle:   ch.Title,
			WorkflowID:     *ws.WorkflowID,
			WorkflowName:   wf.Name,
			CurrentStage:   ws.CurrentStage,
			StageName:      stageName,
			SLASeconds:     slaSeconds,
			LastActivityAt: "",
		}

		// Compute ages
		inst.AgeSeconds = int64(now.Sub(ch.CreatedAt).Seconds())

		if ws.LastActivityAt != nil {
			inst.LastActivityAt = *ws.LastActivityAt
		}

		// Stage age from stage_entered_at
		if ws.StageEnteredAt != nil {
			if t, err := time.Parse(time.RFC3339, *ws.StageEnteredAt); err == nil {
				inst.StageAgeSeconds = int64(now.Sub(t).Seconds())

				// SLA computation
				if slaSeconds != nil {
					remaining := int64(*slaSeconds) - inst.StageAgeSeconds
					inst.SLARemaining = &remaining
					inst.SLABreached = remaining < 0
				}
			}
		}

		result = append(result, inst)
	}

	if result == nil {
		result = []MonitorInstance{}
	}
	return result, nil
}

