package flow

import (
	"bytes"
	"encoding/json"
	"strings"
	"testing"
	"time"

	aiflow "go-micro.dev/v6/flow"
)

func TestWriteFlowRunsIncludesStepDetails(t *testing.T) {
	updated := time.Date(2026, 6, 24, 12, 30, 0, 0, time.UTC)
	runs := []aiflow.Run{{
		ID:      "1234567890abcdef",
		Status:  "failed",
		Updated: updated,
		State:   aiflow.State{Stage: "charge"},
		Steps: []aiflow.StepRecord{
			{Name: "reserve", Status: "done", Attempts: 1},
			{Name: "charge", Status: "failed", Attempts: 3, Error: "card declined"},
		},
	}}

	var out bytes.Buffer
	if err := writeFlowRuns(&out, runs, false); err != nil {
		t.Fatalf("writeFlowRuns: %v", err)
	}
	got := out.String()
	for _, want := range []string{
		"1 run",
		"12345678  failed   stage=charge",
		"updated=2026-06-24T12:30:00Z",
		"- reserve      done        attempts=1",
		`- charge       failed      attempts=3 error="card declined"`,
	} {
		if !strings.Contains(got, want) {
			t.Fatalf("output missing %q:\n%s", want, got)
		}
	}
}

func TestValidateFlowRunOptionsRejectsInvalidStatus(t *testing.T) {
	_, err := validateFlowRunOptions(flowRunOptions{Status: "stuck"})
	if err == nil || !strings.Contains(err.Error(), "invalid run status") {
		t.Fatalf("expected invalid status error, got %v", err)
	}
}

func TestValidateFlowRunOptionsRejectsNegativeLimit(t *testing.T) {
	_, err := validateFlowRunOptions(flowRunOptions{Limit: -1})
	if err == nil || !strings.Contains(err.Error(), "invalid limit") {
		t.Fatalf("expected invalid limit error, got %v", err)
	}
}

func TestWriteFlowRunsJSON(t *testing.T) {
	runs := []aiflow.Run{{ID: "run-1", Flow: "checkout", Status: "done"}}

	var out bytes.Buffer
	if err := writeFlowRuns(&out, runs, true); err != nil {
		t.Fatalf("writeFlowRuns: %v", err)
	}
	var got []aiflow.Run
	if err := json.Unmarshal(out.Bytes(), &got); err != nil {
		t.Fatalf("invalid JSON: %v\n%s", err, out.String())
	}
	if len(got) != 1 || got[0].ID != "run-1" || got[0].Flow != "checkout" || got[0].Status != "done" {
		t.Fatalf("decoded runs = %+v", got)
	}
}

func TestPendingFlowRunsFiltersCompletedRuns(t *testing.T) {
	runs := []aiflow.Run{
		{ID: "run-1", Status: "done"},
		{ID: "run-2", Status: "failed"},
		{ID: "run-3", Status: "running"},
	}

	got := pendingFlowRuns(runs)
	if len(got) != 2 {
		t.Fatalf("pendingFlowRuns returned %d runs, want 2: %+v", len(got), got)
	}
	if got[0].ID != "run-2" || got[1].ID != "run-3" {
		t.Fatalf("pending runs = %+v", got)
	}
}

func TestFilterFlowRunsStatus(t *testing.T) {
	runs := []aiflow.Run{
		{ID: "run-1", Status: "done"},
		{ID: "run-2", Status: "failed"},
		{ID: "run-3", Status: "running"},
		{ID: "run-4", Status: "failed"},
	}

	got := filterFlowRuns(runs, flowRunOptions{Status: "failed"})
	if len(got) != 2 {
		t.Fatalf("filterFlowRuns returned %d runs, want 2: %+v", len(got), got)
	}
	if got[0].ID != "run-2" || got[1].ID != "run-4" {
		t.Fatalf("failed runs = %+v", got)
	}
}

func TestFilterFlowRunsStage(t *testing.T) {
	runs := []aiflow.Run{
		{ID: "run-1", Status: "failed", State: aiflow.State{Stage: "reserve"}},
		{ID: "run-2", Status: "failed", State: aiflow.State{Stage: "charge"}},
		{ID: "run-3", Status: "running", State: aiflow.State{Stage: "charge"}},
		{ID: "run-4", Status: "done", State: aiflow.State{}},
	}

	got := filterFlowRuns(runs, flowRunOptions{Stage: "charge"})
	if len(got) != 2 {
		t.Fatalf("filterFlowRuns returned %d runs, want 2: %+v", len(got), got)
	}
	if got[0].ID != "run-2" || got[1].ID != "run-3" {
		t.Fatalf("charge-stage runs = %+v", got)
	}
}

func TestFilterFlowRunsLimitKeepsNewestRuns(t *testing.T) {
	runs := []aiflow.Run{
		{ID: "run-1", Status: "done"},
		{ID: "run-2", Status: "failed"},
		{ID: "run-3", Status: "running"},
	}

	got := filterFlowRuns(runs, flowRunOptions{Limit: 2})
	if len(got) != 2 {
		t.Fatalf("filterFlowRuns returned %d runs, want 2: %+v", len(got), got)
	}
	if got[0].ID != "run-2" || got[1].ID != "run-3" {
		t.Fatalf("limited runs = %+v", got)
	}
}

func TestFilterFlowRunsCombinesPendingStatusAndLimit(t *testing.T) {
	runs := []aiflow.Run{
		{ID: "run-1", Status: "failed"},
		{ID: "run-2", Status: "done"},
		{ID: "run-3", Status: "failed"},
		{ID: "run-4", Status: "running"},
		{ID: "run-5", Status: "failed"},
	}

	got := filterFlowRuns(runs, flowRunOptions{Pending: true, Status: "failed", Limit: 2})
	if len(got) != 2 {
		t.Fatalf("filterFlowRuns returned %d runs, want 2: %+v", len(got), got)
	}
	if got[0].ID != "run-3" || got[1].ID != "run-5" {
		t.Fatalf("filtered runs = %+v", got)
	}
}
