package board

import (
	"strings"
	"testing"

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

var validBoard = `# Task Board — Milestone T1: Production Hardening

**Submilestone:** T1 (Error recovery, observability, metrics, reliability)

| ID | Scope | Status | Branch | Verification |
|----|-------|--------|--------|--------------|
| T1.1 | Workflow error recovery | [x] | task/T1.1 | Verify recovery works |
| T1.2 | Structured logging | [x] | task/T1.2 | Verify metrics visible |
| T1.3 | Timeout tuning | [x] | task/T1.3 | Verify recommendations |
| T1.4 | Board validation | [ ] | task/T1.4 | Verify healing works |
`

func TestValidateValidBoard(t *testing.T) {
	bv := NewBoardValidator("")
	valid := bv.ValidateBoard(validBoard)
	assert.True(t, valid)
	assert.False(t, bv.HasErrors())
}

func TestValidateInvalidStatus(t *testing.T) {
	board := strings.ReplaceAll(validBoard, "[x]", "[?]")
	bv := NewBoardValidator("")
	valid := bv.ValidateBoard(board)
	assert.False(t, valid)
	assert.True(t, bv.HasErrors())
}

func TestValidateMissingHeader(t *testing.T) {
	boardNoHeader := `| ID | Scope | Status | Branch | Verification |
|----|-------|--------|--------|--------------|
| T1.1 | Task | [x] | branch | verify |
`
	bv := NewBoardValidator("")
	valid := bv.ValidateBoard(boardNoHeader)
	assert.False(t, valid)
	assert.True(t, bv.HasErrors())
}

func TestParseTasks(t *testing.T) {
	bv := NewBoardValidator("")
	tasks, err := bv.ParseTasks(validBoard)
	assert.NoError(t, err)
	assert.Equal(t, 4, len(tasks))
	assert.Equal(t, "T1.1", tasks[0].ID)
	assert.Equal(t, "[x]", tasks[0].Status)
}

func TestErrorSummary(t *testing.T) {
	board := strings.ReplaceAll(validBoard, "[x]", "[?]")
	bv := NewBoardValidator("")
	bv.ValidateBoard(board)

	summary := bv.ErrorSummary()
	assert.Contains(t, summary, "error")
}

func TestRepairBoard(t *testing.T) {
	boardNoHeader := `| T1.1 | Task | [ ] | branch | verify |`

	bv := NewBoardValidator("")
	repaired, err := bv.RepairBoard(boardNoHeader)
	assert.NoError(t, err)
	assert.Contains(t, repaired, "Task Board")
}

func TestIsValidTaskID(t *testing.T) {
	assert.True(t, isValidTaskID("T0"))
	assert.True(t, isValidTaskID("T1"))
	assert.True(t, isValidTaskID("T1.1"))
	assert.True(t, isValidTaskID("T1.8"))
	assert.False(t, isValidTaskID("Task1"))
	assert.False(t, isValidTaskID("T"))
}

func TestDetectDivergence(t *testing.T) {
	bv := NewBoardValidator("")

	actualStates := map[string]bool{
		"T1.1": true,  // Completed in reality
		"T1.2": true,  // Completed in reality
		"T1.3": true,  // Completed in reality
		"T1.4": false, // Not completed in reality
	}

	// Valid board has T1.1, T1.2, T1.3 as [x] and T1.4 as [ ]
	divergences := bv.DetectDivergence(validBoard, actualStates)

	// Should be no divergences since they match
	assert.Equal(t, 0, len(divergences))
}

func TestDetectDivergenceWithMismatch(t *testing.T) {
	bv := NewBoardValidator("")

	actualStates := map[string]bool{
		"T1.1": false, // Should be true but is false
		"T1.2": true,
		"T1.3": true,
		"T1.4": true,  // Should be false but is true
	}

	divergences := bv.DetectDivergence(validBoard, actualStates)

	// Should find 2 divergences
	assert.Greater(t, len(divergences), 0)
}

func TestHealDivergence(t *testing.T) {
	bv := NewBoardValidator("")

	actualStates := map[string]bool{
		"T1.1": false, // Different from board
		"T1.2": true,
		"T1.3": true,
		"T1.4": true,  // Different from board
	}

	healed, changes, err := bv.HealDivergence(validBoard, actualStates)
	assert.NoError(t, err)
	assert.Greater(t, len(changes), 0)

	// Verify healing worked
	bv2 := NewBoardValidator("")
	tasks, _ := bv2.ParseTasks(healed)
	for _, task := range tasks {
		expected, _ := actualStates[task.ID]
		if expected {
			assert.Equal(t, "[x]", task.Status)
		} else {
			assert.Equal(t, "[ ]", task.Status)
		}
	}
}

func TestParseTasksEmptyBoard(t *testing.T) {
	bv := NewBoardValidator("")
	tasks, err := bv.ParseTasks("")
	assert.Error(t, err)
	assert.Equal(t, 0, len(tasks))
}

func TestValidateEmptyBoard(t *testing.T) {
	bv := NewBoardValidator("")
	valid := bv.ValidateBoard("")
	assert.False(t, valid)
	assert.True(t, bv.HasErrors())
}

func TestWarningsSummary(t *testing.T) {
	bv := NewBoardValidator("")
	bv.validateTaskRow("| ABC | Description | [x] | branch | verify |", 1)

	summary := bv.WarningsSummary()
	assert.Contains(t, summary, "Invalid task ID")
}

func TestMultipleTasks(t *testing.T) {
	bv := NewBoardValidator("")
	tasks, err := bv.ParseTasks(validBoard)
	assert.NoError(t, err)

	for _, task := range tasks {
		assert.NotEmpty(t, task.ID)
		assert.NotEmpty(t, task.Status)
	}
}

func TestNormalizeStatus(t *testing.T) {
	board := strings.ReplaceAll(validBoard, "[x]", "[X]")
	bv := NewBoardValidator("")
	_, _ = bv.RepairBoard(board)
	// Should normalize [X] to [x]
}
