package rules

import (
	"context"
	"encoding/json"
	"fmt"
)

type WorkflowSimulationStep struct {
	NodeID   string `json:"node_id"`
	NodeType string `json:"node_type"`
	Handle   string `json:"handle,omitempty"`
}

type WorkflowSimulationResult struct {
	Matched bool                    `json:"matched"`
	Steps   []WorkflowSimulationStep `json:"steps,omitempty"`
	Actions []SimulatedActionResult  `json:"actions,omitempty"`
}

func (e *Engine) SimulateWorkflow(ctx context.Context, userID string, wf *Workflow, msg *Message, evt *EventContext) WorkflowSimulationResult {
	if wf == nil || len(wf.Nodes) == 0 {
		return WorkflowSimulationResult{Matched: false}
	}
	if wf.Kind != RuleKindFunction && !matchesTriggers(wf.Triggers, msg, evt) {
		return WorkflowSimulationResult{Matched: false}
	}
	startID := wf.findStartNode()
	if startID == "" {
		return WorkflowSimulationResult{Matched: false}
	}
	execCtx := newExecContext(msg, userID, wf.Variables, evt)
	steps := make([]WorkflowSimulationStep, 0)
	e.simulateWalk(ctx, userID, msg, wf, startID, execCtx, &steps, 0)
	simActions := make([]SimulatedActionResult, 0, len(execCtx.Results))
	for _, r := range execCtx.Results {
		simActions = append(simActions, SimulatedActionResult{ActionResult: r})
	}
	return WorkflowSimulationResult{
		Matched: true,
		Steps:   steps,
		Actions: simActions,
	}
}

func (e *Engine) simulateWalk(ctx context.Context, userID string, msg *Message, wf *Workflow, nodeID string, execCtx *ExecContext, steps *[]WorkflowSimulationStep, depth int) {
	if depth > maxWorkflowDepth || nodeID == "" {
		return
	}
	nodes := wf.nodeMap()
	node, ok := nodes[nodeID]
	if !ok {
		return
	}

	switch node.Type {
	case "start":
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type})
		e.simulateWalk(ctx, userID, msg, wf, wf.nextDefault(nodeID), execCtx, steps, depth+1)

	case "condition":
		var data ConditionNodeData
		json.Unmarshal(node.Data, &data)
		handle := "false"
		if matchCondition(Condition{Field: data.Field, Operator: data.Operator, Value: interpolateValue(data.Value, execCtx)}, msg, execCtx.Event) {
			handle = "true"
		}
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type, Handle: handle})
		e.simulateWalk(ctx, userID, msg, wf, wf.nextNode(nodeID, handle), execCtx, steps, depth+1)

	case "label_check":
		var data LabelCheckNodeData
		json.Unmarshal(node.Data, &data)
		op := "has"
		if data.Operator == "not_has" {
			op = "not_has"
		}
		handle := "false"
		if matchCondition(Condition{Field: "label", Operator: op, Value: data.Label}, msg, execCtx.Event) {
			handle = "true"
		}
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type, Handle: handle})
		e.simulateWalk(ctx, userID, msg, wf, wf.nextNode(nodeID, handle), execCtx, steps, depth+1)

	case "switch":
		var data SwitchNodeData
		json.Unmarshal(node.Data, &data)
		fieldVal := workflowFieldValue(data.Field, msg, execCtx.Event, execCtx)
		handle := "default"
		for i, c := range data.Cases {
			if fieldVal == c.Value {
				handle = fmt.Sprintf("case-%d", i)
				break
			}
		}
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type, Handle: handle})
		next := wf.nextNode(nodeID, handle)
		if next == "" {
			next = wf.nextNode(nodeID, "default")
		}
		e.simulateWalk(ctx, userID, msg, wf, next, execCtx, steps, depth+1)

	case "llm_check":
		var data LLMCheckNodeData
		json.Unmarshal(node.Data, &data)
		handle := "false"
		if e.evaluateLLMCheck(ctx, data, msg, execCtx) {
			handle = "true"
		}
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type, Handle: handle})
		e.simulateWalk(ctx, userID, msg, wf, wf.nextNode(nodeID, handle), execCtx, steps, depth+1)

	case "actions":
		var data ActionsNodeData
		json.Unmarshal(node.Data, &data)
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type})
		for _, item := range data.Actions {
			action := Action{Type: item.Type, Value: interpolateValue(item.Value, execCtx)}
			execCtx.Results = append(execCtx.Results, e.simulateAction(ctx, action, msg).ActionResult)
		}
		e.simulateWalk(ctx, userID, msg, wf, wf.nextDefault(nodeID), execCtx, steps, depth+1)

	case "set_var":
		var data SetVarNodeData
		json.Unmarshal(node.Data, &data)
		execCtx.Variables[data.Name] = interpolateValue(data.Value, execCtx)
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type})
		e.simulateWalk(ctx, userID, msg, wf, wf.nextDefault(nodeID), execCtx, steps, depth+1)

	case "call_function", "call_rule":
		var data CallRuleNodeData
		json.Unmarshal(node.Data, &data)
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type})
		e.simulateWalk(ctx, userID, msg, wf, wf.nextDefault(nodeID), execCtx, steps, depth+1)

	case "end":
		*steps = append(*steps, WorkflowSimulationStep{NodeID: nodeID, NodeType: node.Type})
	}
}
