package registry

import (
	"fmt"
	"os"
	"regexp"
	"strings"
)

// testCase represents a single test extracted from a test file.
type testCase struct {
	Name string
	Code string
}

// testPos marks the start of a test within a file.
type testPos struct {
	name      string
	startLine int
}

// parseTestFile reads a test file and extracts individual test cases.
// Supports Go, Python, and Bash test formats.
func parseTestFile(path, lang string) ([]testCase, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("reading test file %s: %w", path, err)
	}
	content := string(data)

	switch lang {
	case "go":
		return parseGoTests(content), nil
	case "py":
		return parsePythonTests(content), nil
	case "bash":
		return parseBashTests(content), nil
	case "cpp":
		return parseCppTests(content), nil
	default:
		return nil, nil
	}
}

// parseGoTests extracts Go test functions (func TestXxx).
var goTestFuncRe = regexp.MustCompile(`(?m)^func\s+(Test\w+)\s*\(`)

func parseGoTests(content string) []testCase {
	lines := strings.Split(content, "\n")
	var positions []testPos

	for i, line := range lines {
		if m := goTestFuncRe.FindStringSubmatch(line); m != nil {
			positions = append(positions, testPos{name: m[1], startLine: i})
		}
	}

	return extractBlocks(lines, positions)
}

// parsePythonTests extracts Python test functions (def test_xxx).
var pyTestFuncRe = regexp.MustCompile(`(?m)^def\s+(test_\w+)\s*\(`)

func parsePythonTests(content string) []testCase {
	lines := strings.Split(content, "\n")
	var positions []testPos

	for i, line := range lines {
		if m := pyTestFuncRe.FindStringSubmatch(line); m != nil {
			positions = append(positions, testPos{name: m[1], startLine: i})
		}
	}

	return extractBlocks(lines, positions)
}

// parseBashTests extracts Bash test blocks.
// Tries three conventions in order:
//  1. test_xxx() { ... } — function-based tests
//  2. === section === — section headers (echo "=== name ===")
//  3. # Test: ... — comment-based test blocks
var bashTestFuncRe = regexp.MustCompile(`(?m)^(test_\w+)\s*\(\)\s*\{`)
var bashTestCommentRe = regexp.MustCompile(`(?m)^#\s*[Tt]est:\s*(.+)`)
var bashSectionRe = regexp.MustCompile(`(?i)^(?:echo\s+["'])?===\s*(\w[\w\s]*\w)\s*===["']?\s*$`)

func parseBashTests(content string) []testCase {
	lines := strings.Split(content, "\n")

	// Strategy 1: test_xxx() { ... }
	var positions []testPos
	for i, line := range lines {
		if m := bashTestFuncRe.FindStringSubmatch(line); m != nil {
			positions = append(positions, testPos{name: m[1], startLine: i})
		}
	}
	if len(positions) > 0 {
		return extractBlocks(lines, positions)
	}

	// Strategy 2: === section === headers
	for i, line := range lines {
		trimmed := strings.TrimSpace(line)
		if m := bashSectionRe.FindStringSubmatch(trimmed); m != nil {
			positions = append(positions, testPos{name: m[1], startLine: i})
		}
	}
	if len(positions) > 0 {
		return extractBlocks(lines, positions)
	}

	// Strategy 3: # Test: ... comments
	for i, line := range lines {
		if m := bashTestCommentRe.FindStringSubmatch(line); m != nil {
			positions = append(positions, testPos{name: strings.TrimSpace(m[1]), startLine: i})
		}
	}
	return extractBlocks(lines, positions)
}

// parseCppTests extracts C++ test functions (Google Test TEST/TEST_F macros).
var cppTestRe = regexp.MustCompile(`(?m)^(?:TEST|TEST_F|TEST_P)\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)`)

func parseCppTests(content string) []testCase {
	lines := strings.Split(content, "\n")
	var positions []testPos

	for i, line := range lines {
		if m := cppTestRe.FindStringSubmatch(line); m != nil {
			name := m[1] + "." + m[2] // Suite.TestName
			positions = append(positions, testPos{name: name, startLine: i})
		}
	}

	return extractBlocks(lines, positions)
}

// extractBlocks splits lines into code blocks based on test positions.
func extractBlocks(lines []string, positions []testPos) []testCase {
	var tests []testCase
	for i, pos := range positions {
		endLine := len(lines)
		if i+1 < len(positions) {
			endLine = positions[i+1].startLine
		}
		for endLine > pos.startLine && strings.TrimSpace(lines[endLine-1]) == "" {
			endLine--
		}
		code := strings.Join(lines[pos.startLine:endLine], "\n")
		tests = append(tests, testCase{Name: pos.name, Code: code})
	}
	return tests
}
