package workflow_test

import (
	"context"
	"encoding/json"
	"testing"

	"workflow"
)

// ---------------------------------------------------------------------------
// BUG-1: StepOutput string shorthand — "out": "$var" must deserialise to
//         {"$var": "=_result"} (spec §5.2.4)
// ---------------------------------------------------------------------------

// TestOutStringShorthandUnmarshal verifies that a JSON string value for "out"
// is accepted and expanded to the canonical map form.
func TestOutStringShorthandUnmarshal(t *testing.T) {
	raw := `{"id":"LLM_Answer","out":"$answer","next":"Stop_End"}`
	var step workflow.Step
	if err := json.Unmarshal([]byte(raw), &step); err != nil {
		t.Fatalf("unexpected unmarshal error: %v", err)
	}
	if len(step.Out) != 1 {
		t.Fatalf("expected Out to have 1 entry, got %d", len(step.Out))
	}
	if step.Out["$answer"] != "=_result" {
		t.Errorf("expected Out[\"$answer\"] == \"=_result\", got %q", step.Out["$answer"])
	}
}

// TestOutStringShorthandEndToEnd verifies that a workflow whose "out" field uses
// the string shorthand actually runs and stores _result into the target variable.
func TestOutStringShorthandEndToEnd(t *testing.T) {
	wf := &workflow.Workflow{
		Version: "3.13",
		Name:    "BUG1 Out Shorthand E2E",
		Registry: workflow.Registry{
			Vars: []string{"$answer(STRING)"},
		},
		Steps: []workflow.Step{
			{
				ID: "LLM_Answer",
				In: workflow.StepInput{
					"messages": []interface{}{
						map[string]interface{}{"role": "user", "content": "hi"},
					},
				},
				Out:  workflow.StepOutput{"$answer": "=_result"}, // canonical form
				Next: "Stop_End",
			},
			{ID: "Stop_End"},
		},
	}

	// Replicate the shorthand by round-tripping through JSON
	jsonBytes, err := json.Marshal(wf.Steps[0])
	if err != nil {
		t.Fatalf("marshal: %v", err)
	}
	// Replace the canonical out object with its string shorthand
	var raw map[string]interface{}
	if err := json.Unmarshal(jsonBytes, &raw); err != nil {
		t.Fatalf("unmarshal to raw: %v", err)
	}
	raw["out"] = "$answer"
	patchedBytes, _ := json.Marshal(raw)
	var patchedStep workflow.Step
	if err := json.Unmarshal(patchedBytes, &patchedStep); err != nil {
		t.Fatalf("unmarshal patched step: %v", err)
	}
	wf.Steps[0] = patchedStep

	adapters := createTestAdapters()
	llm := adapters.LLM.(*workflow.DefaultLLMAdapter)
	llm.SetHandler(func(ctx context.Context, params map[string]interface{}, stream chan<- string) (map[string]interface{}, error) {
		return map[string]interface{}{
			"content":       "hello world",
			"finish_reason": "stop",
			"model":         "test-model",
		}, nil
	})

	eng, err := workflow.NewEngine(wf)
	if err != nil {
		t.Fatalf("NewEngine: %v", err)
	}
	result, err := eng.Execute(context.Background(), nil, adapters)
	if err != nil {
		t.Fatalf("Execute: %v", err)
	}
	for range result.RunEventStream {
	}

	vars := result.Context.Variables
	if vars["$answer"] != "hello world" {
		t.Errorf("expected $answer == \"hello world\", got %v", vars["$answer"])
	}
}

// ---------------------------------------------------------------------------
// BUG-2: Branch → Stop_* must not trigger a second workflow_done
// ---------------------------------------------------------------------------

// makeBug2Workflow builds the workflow described in the bug report:
//
//	Branch_Go
//	  case true  → Stop_A   (the branch that fires)
//	  ELSE       → Stop_B   (never reached)
//	next         → Stop_C   (must NOT be reached after Stop_A fires)
func makeBug2Workflow() *workflow.Workflow {
	return &workflow.Workflow{
		Version: "3.13",
		Name:    "BUG2 Double WorkflowDone",
		Registry: workflow.Registry{
			Vars: []string{"$x(STRING)"},
		},
		Steps: []workflow.Step{
			{
				ID: "Branch_Go",
				Cases: [][]string{
					{"true", "Stop_A"},
					{"ELSE", "Stop_B"},
				},
				Next: "Stop_C",
			},
			{ID: "Stop_A"},
			{ID: "Stop_B"},
			{ID: "Stop_C"},
		},
	}
}

// TestBranchStopNoDoubleWorkflowDone verifies that when a Branch case directly
// targets a Stop_* node exactly one workflow_done event is emitted and the
// Branch's next pointer is never followed.
func TestBranchStopNoDoubleWorkflowDone(t *testing.T) {
	wf := makeBug2Workflow()

	eng, err := workflow.NewEngine(wf)
	if err != nil {
		t.Fatalf("NewEngine: %v", err)
	}
	result, err := eng.Execute(context.Background(), nil, createTestAdapters())
	if err != nil {
		t.Fatalf("Execute: %v", err)
	}

	var events []workflow.RunEvent
	for ev := range result.RunEventStream {
		events = append(events, ev)
	}

	// Count workflow_done events — must be exactly 1
	doneCount := 0
	var doneStopID string
	for _, ev := range events {
		if ev.Type == workflow.RunEventWorkflowDone {
			doneCount++
			if sid, ok := ev.Payload["stop_id"].(string); ok {
				doneStopID = sid
			}
		}
	}
	if doneCount != 1 {
		t.Errorf("expected exactly 1 workflow_done event, got %d", doneCount)
		for _, ev := range events {
			t.Logf("  seq=%d type=%s step_id=%v payload=%v", ev.Seq, ev.Type, ev.StepID, ev.Payload)
		}
	}
	if doneStopID != "Stop_A" {
		t.Errorf("expected workflow_done stop_id=Stop_A, got %q", doneStopID)
	}

	// Stop_C must never have started
	for _, ev := range events {
		if ev.Type == workflow.RunEventStepStart && ev.StepID != nil && *ev.StepID == "Stop_C" {
			t.Errorf("Stop_C was started — it should have been suppressed after Stop_A fired")
		}
	}
}
