package board

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestStateTracker(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	// Update a task state
	err := st.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)
	assert.NoError(t, err)

	// Retrieve the state
	state := st.GetTaskState("T1.1")
	assert.NotNil(t, state)
	assert.Equal(t, "T1.1", state.TaskID)
	assert.Equal(t, "completed", state.Status)
	assert.NotZero(t, state.CompletedAt)
}

func TestGetAllStates(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	st.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)
	st.UpdateTaskState("T1.2", "in_progress", "task/T1.2", nil)
	st.UpdateTaskState("T1.3", "pending", "task/T1.3", nil)

	states := st.GetAllStates()
	assert.Equal(t, 3, len(states))
}

func TestGetCompletedTasks(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	st.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)
	st.UpdateTaskState("T1.2", "completed", "task/T1.2", nil)
	st.UpdateTaskState("T1.3", "pending", "task/T1.3", nil)

	completed := st.GetCompletedTasks()
	assert.Equal(t, 2, len(completed))
	assert.Contains(t, completed, "T1.1")
	assert.Contains(t, completed, "T1.2")
}

func TestGetFailedTasks(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	err := assert.AnError
	st.UpdateTaskState("T1.1", "failed", "task/T1.1", err)
	st.UpdateTaskState("T1.2", "completed", "task/T1.2", nil)

	failed := st.GetFailedTasks()
	assert.Equal(t, 1, len(failed))
	assert.Equal(t, "T1.1", failed[0])
}

func TestGetPendingTasks(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	st.UpdateTaskState("T1.1", "pending", "task/T1.1", nil)
	st.UpdateTaskState("T1.2", "in_progress", "task/T1.2", nil)
	st.UpdateTaskState("T1.3", "completed", "task/T1.3", nil)

	pending := st.GetPendingTasks()
	assert.Equal(t, 2, len(pending))
}

func TestAddMetric(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	st.UpdateTaskState("T1.1", "in_progress", "task/T1.1", nil)
	err := st.AddMetric("T1.1", "duration_seconds", 42.5)
	assert.NoError(t, err)

	state := st.GetTaskState("T1.1")
	assert.NotNil(t, state.Metrics["duration_seconds"])
	assert.Equal(t, 42.5, state.Metrics["duration_seconds"])
}

func TestAddMetricNonexistent(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	err := st.AddMetric("nonexistent", "metric", 123)
	assert.Error(t, err)
}

func TestPersistence(t *testing.T) {
	tmpDir := t.TempDir()
	st1 := NewStateTracker(tmpDir)

	st1.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)
	st1.UpdateTaskState("T1.2", "pending", "task/T1.2", nil)

	// Create new instance and load
	st2 := NewStateTracker(tmpDir)
	err := st2.Load()
	assert.NoError(t, err)

	states := st2.GetAllStates()
	assert.Equal(t, 2, len(states))
	assert.Equal(t, "completed", states["T1.1"].Status)
	assert.Equal(t, "pending", states["T1.2"].Status)
}

func TestGetAsCompletionMap(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	st.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)
	st.UpdateTaskState("T1.2", "pending", "task/T1.2", nil)
	st.UpdateTaskState("T1.3", "failed", "task/T1.3", assert.AnError)

	completion := st.GetAsCompletionMap()
	assert.Equal(t, true, completion["T1.1"])
	assert.Equal(t, false, completion["T1.2"])
	assert.Equal(t, false, completion["T1.3"])
}

func TestGetLastUpdate(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	before := time.Now()
	st.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)
	after := time.Now()

	lastUpdate := st.GetLastUpdate()
	assert.True(t, lastUpdate.After(before) || lastUpdate.Equal(before))
	assert.True(t, lastUpdate.Before(after) || lastUpdate.Equal(after))
}

func TestGetStats(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	st.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)
	st.UpdateTaskState("T1.2", "completed", "task/T1.2", nil)
	st.UpdateTaskState("T1.3", "pending", "task/T1.3", nil)
	st.UpdateTaskState("T1.4", "failed", "task/T1.4", assert.AnError)

	stats := st.GetStats()
	assert.Equal(t, 4, stats["total"])

	counts := stats["counts"].(map[string]int)
	assert.Equal(t, 2, counts["completed"])
	assert.Equal(t, 1, counts["pending"])
	assert.Equal(t, 1, counts["failed"])
}

func TestReset(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	st.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)
	st.UpdateTaskState("T1.2", "pending", "task/T1.2", nil)

	assert.Equal(t, 2, len(st.GetAllStates()))

	err := st.Reset()
	assert.NoError(t, err)
	assert.Equal(t, 0, len(st.GetAllStates()))
}

func TestTaskStateFields(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	err := assert.AnError
	st.UpdateTaskState("T1.1", "failed", "task/T1.1", err)

	state := st.GetTaskState("T1.1")
	assert.Equal(t, "T1.1", state.TaskID)
	assert.Equal(t, "failed", state.Status)
	assert.Equal(t, "task/T1.1", state.Branch)
	assert.NotEmpty(t, state.Error)
	assert.NotZero(t, state.FailedAt)
}

func TestLoadNonexistentState(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	// Should not error when file doesn't exist
	err := st.Load()
	assert.NoError(t, err)
	assert.Equal(t, 0, len(st.GetAllStates()))
}

func TestMultipleStateUpdates(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	// Task progresses through states
	st.UpdateTaskState("T1.1", "pending", "task/T1.1", nil)
	state1 := st.GetTaskState("T1.1")

	time.Sleep(10 * time.Millisecond)

	st.UpdateTaskState("T1.1", "in_progress", "task/T1.1", nil)
	state2 := st.GetTaskState("T1.1")

	// Status should be updated
	assert.Equal(t, "pending", state1.Status)
	assert.Equal(t, "in_progress", state2.Status)
}

func TestStateFileLayout(t *testing.T) {
	tmpDir := t.TempDir()
	st := NewStateTracker(tmpDir)

	st.UpdateTaskState("T1.1", "completed", "task/T1.1", nil)

	// Verify state was tracked
	state := st.GetTaskState("T1.1")
	assert.NotNil(t, state)
}
