package board

import (
	"fmt"
	"regexp"
	"strings"
	"time"
)

// BoardValidationError represents a validation error
type BoardValidationError struct {
	Type    string // "missing_header", "invalid_row", "malformed_table", etc.
	Message string
	Line    int
	Context string
}

// BoardValidator validates and repairs board files
type BoardValidator struct {
	boardPath string
	errors    []BoardValidationError
	warnings  []string
}

// NewBoardValidator creates a new board validator
func NewBoardValidator(boardPath string) *BoardValidator {
	return &BoardValidator{
		boardPath: boardPath,
		errors:    make([]BoardValidationError, 0),
		warnings:  make([]string, 0),
	}
}

// TaskRow represents a parsed task row from the board
type TaskRow struct {
	ID          string
	Description string
	Status      string // "[x]", "[ ]"
	Branch      string
	Verification string
	LineNo      int
}

// ValidateBoard validates the board structure
func (bv *BoardValidator) ValidateBoard(content string) bool {
	bv.errors = make([]BoardValidationError, 0)
	bv.warnings = make([]string, 0)

	lines := strings.Split(content, "\n")

	// Check for required headers
	if !bv.hasValidHeader(lines) {
		bv.errors = append(bv.errors, BoardValidationError{
			Type:    "missing_header",
			Message: "Board must have a valid markdown header",
		})
		return false
	}

	// Check for table separator
	if !bv.hasTableSeparator(lines) {
		bv.errors = append(bv.errors, BoardValidationError{
			Type:    "missing_table_separator",
			Message: "Board must have a markdown table separator line (|---|---|...)",
		})
		return false
	}

	// Validate task rows
	tableStartIdx := bv.findTableStart(lines)
	if tableStartIdx >= 0 {
		bv.validateTaskRows(lines[tableStartIdx:], tableStartIdx)
	}

	return len(bv.errors) == 0
}

// hasValidHeader checks if the board has a valid header
func (bv *BoardValidator) hasValidHeader(lines []string) bool {
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") && strings.Contains(line, "Task Board") {
			return true
		}
	}
	return false
}

// hasTableSeparator checks if the board has a table separator
func (bv *BoardValidator) hasTableSeparator(lines []string) bool {
	for _, line := range lines {
		if strings.Contains(line, "|") && strings.Contains(line, "-") && strings.Contains(line, "-|-") {
			return true
		}
	}
	return false
}

// findTableStart finds the start of the task table
func (bv *BoardValidator) findTableStart(lines []string) int {
	for i, line := range lines {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "|") && !strings.Contains(line, "---") && !strings.Contains(line, "ID") {
			continue
		}
		if strings.HasPrefix(line, "|") && strings.Contains(line, "ID") {
			return i + 2 // Skip header and separator
		}
	}
	return -1
}

// validateTaskRows validates all task rows in the table
func (bv *BoardValidator) validateTaskRows(lines []string, startIdx int) {
	for i, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" || !strings.HasPrefix(line, "|") {
			break
		}

		if strings.Contains(line, "---") {
			continue // Skip separator
		}

		lineNo := startIdx + i
		err := bv.validateTaskRow(line, lineNo)
		if err.Message != "" {
			bv.errors = append(bv.errors, err)
		}
	}
}

// validateTaskRow validates a single task row
func (bv *BoardValidator) validateTaskRow(line string, lineNo int) BoardValidationError {
	parts := strings.Split(line, "|")

	// Should have at least 6 parts: [empty, ID, Desc, Status, Branch, Verif, empty]
	if len(parts) < 6 {
		return BoardValidationError{
			Type:    "invalid_row",
			Message: fmt.Sprintf("Invalid row format (expected at least 5 columns, got %d)", len(parts)-2),
			Line:    lineNo,
			Context: line,
		}
	}

	id := strings.TrimSpace(parts[1])
	status := strings.TrimSpace(parts[3])

	// Validate ID (should be T1.1 format or similar)
	if !isValidTaskID(id) {
		bv.warnings = append(bv.warnings, fmt.Sprintf("Line %d: Invalid task ID format: %s", lineNo, id))
	}

	// Validate status (should be [x] or [ ])
	if status != "[x]" && status != "[ ]" && status != "[X]" {
		return BoardValidationError{
			Type:    "invalid_status",
			Message: fmt.Sprintf("Status must be '[x]' or '[ ]', got '%s'", status),
			Line:    lineNo,
			Context: line,
		}
	}

	return BoardValidationError{} // Valid
}

// isValidTaskID checks if a task ID is valid
func isValidTaskID(id string) bool {
	// Match patterns like T0, T1.1, T1.2, etc.
	pattern := regexp.MustCompile(`^T\d+(\.\d+)?$`)
	return pattern.MatchString(id)
}

// ParseTasks parses all tasks from board content
func (bv *BoardValidator) ParseTasks(content string) ([]TaskRow, error) {
	lines := strings.Split(content, "\n")
	tasks := make([]TaskRow, 0)

	tableStartIdx := bv.findTableStart(lines)
	if tableStartIdx < 0 {
		return nil, fmt.Errorf("no task table found")
	}

	for i := tableStartIdx; i < len(lines); i++ {
		line := strings.TrimSpace(lines[i])

		if line == "" || !strings.HasPrefix(line, "|") {
			break
		}

		if strings.Contains(line, "---") {
			continue
		}

		parts := strings.Split(line, "|")
		if len(parts) < 6 {
			continue
		}

		task := TaskRow{
			ID:           strings.TrimSpace(parts[1]),
			Description:  strings.TrimSpace(parts[2]),
			Status:       strings.TrimSpace(parts[3]),
			Branch:       strings.TrimSpace(parts[4]),
			Verification: strings.TrimSpace(parts[5]),
			LineNo:       i,
		}

		if task.ID != "" {
			tasks = append(tasks, task)
		}
	}

	return tasks, nil
}

// GetErrors returns validation errors
func (bv *BoardValidator) GetErrors() []BoardValidationError {
	return bv.errors
}

// GetWarnings returns validation warnings
func (bv *BoardValidator) GetWarnings() []string {
	return bv.warnings
}

// HasErrors checks if there are any errors
func (bv *BoardValidator) HasErrors() bool {
	return len(bv.errors) > 0
}

// ErrorSummary returns a summary of errors
func (bv *BoardValidator) ErrorSummary() string {
	if len(bv.errors) == 0 {
		return "No errors found"
	}

	summary := fmt.Sprintf("Found %d error(s):\n", len(bv.errors))
	for i, err := range bv.errors {
		summary += fmt.Sprintf("%d. [Line %d] %s: %s\n", i+1, err.Line, err.Type, err.Message)
		if err.Context != "" {
			summary += fmt.Sprintf("   Context: %s\n", err.Context)
		}
	}

	return summary
}

// Warnings returns all warnings
func (bv *BoardValidator) WarningsSummary() string {
	if len(bv.warnings) == 0 {
		return "No warnings found"
	}

	summary := fmt.Sprintf("Found %d warning(s):\n", len(bv.warnings))
	for i, warn := range bv.warnings {
		summary += fmt.Sprintf("%d. %s\n", i+1, warn)
	}

	return summary
}

// RepairBoard attempts to repair common board issues
func (bv *BoardValidator) RepairBoard(content string) (string, error) {
	lines := strings.Split(content, "\n")

	// Add header if missing
	if !bv.hasValidHeader(lines) {
		newLines := make([]string, 0)
		newLines = append(newLines, "# Task Board — Milestone T1: Production Hardening")
		newLines = append(newLines, "")
		newLines = append(newLines, "**Submilestone:** T1 (Error recovery, observability, metrics, reliability)")
		newLines = append(newLines, "")
		newLines = append(newLines, lines...)
		lines = newLines
	}

	// Add table separator if missing
	if !bv.hasTableSeparator(lines) {
		for i, line := range lines {
			if strings.HasPrefix(line, "|") && strings.Contains(line, "ID") {
				// Insert separator after header
				newLines := make([]string, 0)
				newLines = append(newLines, lines[:i+1]...)
				newLines = append(newLines, "|---|---|---|---|---|")
				newLines = append(newLines, lines[i+1:]...)
				lines = newLines
				break
			}
		}
	}

	// Repair invalid status values
	for i, line := range lines {
		if strings.Contains(line, "|") && !strings.Contains(line, "---|") {
			// Replace invalid status markers
			line = strings.ReplaceAll(line, "[ ]", "[ ]") // Normalize
			line = strings.ReplaceAll(line, "[X]", "[x]") // Normalize
			lines[i] = line
		}
	}

	return strings.Join(lines, "\n"), nil
}

// BoardDivergence represents a difference between expected and actual state
type BoardDivergence struct {
	TaskID       string
	ExpectedStatus string
	ActualStatus   string
	DiscoveredAt   time.Time
}

// DetectDivergence detects differences between expected and actual task states
func (bv *BoardValidator) DetectDivergence(content string, actualStates map[string]bool) []BoardDivergence {
	tasks, err := bv.ParseTasks(content)
	if err != nil {
		return nil
	}

	divergences := make([]BoardDivergence, 0)

	for _, task := range tasks {
		expectedComplete := task.Status == "[x]"
		actualComplete, exists := actualStates[task.ID]

		if !exists {
			// Task not in actual state - assume not complete
			actualComplete = false
		}

		if expectedComplete != actualComplete {
			divergences = append(divergences, BoardDivergence{
				TaskID:         task.ID,
				ExpectedStatus: fmt.Sprintf("%v", expectedComplete),
				ActualStatus:   fmt.Sprintf("%v", actualComplete),
				DiscoveredAt:   time.Now(),
			})
		}
	}

	return divergences
}

// HealDivergence updates board to match actual state
func (bv *BoardValidator) HealDivergence(content string, actualStates map[string]bool) (string, []string, error) {
	lines := strings.Split(content, "\n")
	changes := make([]string, 0)

	for i, line := range lines {
		if !strings.HasPrefix(strings.TrimSpace(line), "|") || strings.Contains(line, "---") || strings.Contains(line, "ID") {
			continue
		}

		parts := strings.Split(line, "|")
		if len(parts) < 4 {
			continue
		}

		taskID := strings.TrimSpace(parts[1])
		currentStatus := strings.TrimSpace(parts[3])

		if actualState, exists := actualStates[taskID]; exists {
			var expectedStatus string
			if actualState {
				expectedStatus = "[x]"
			} else {
				expectedStatus = "[ ]"
			}

			if currentStatus != expectedStatus {
				// Update the status
				parts[3] = " " + expectedStatus + " "
				lines[i] = strings.Join(parts, "|")
				changes = append(changes, fmt.Sprintf("Fixed %s: %s → %s", taskID, currentStatus, expectedStatus))
			}
		}
	}

	return strings.Join(lines, "\n"), changes, nil
}
