package git

import (
	"bufio"
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

// ChangedLine represents a single line change from git diff
type ChangedLine struct {
	File       string
	LineNumber int
	Content    string
	ChangeType string // "+" for addition, "~" for modification
}

// DiffResult contains all changed lines from a git diff
type DiffResult struct {
	Changes []ChangedLine
}

// GetUnstagedDiff gets the git diff for unstaged changes
func GetUnstagedDiff() (*DiffResult, error) {
	cmd := exec.Command("git", "diff", "--unified=0")
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to run git diff: %w", err)
	}

	return parseDiff(string(output))
}

// GetStagedDiff gets the git diff for staged changes
func GetStagedDiff() (*DiffResult, error) {
	cmd := exec.Command("git", "diff", "--cached", "--unified=0")
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to run git diff --cached: %w", err)
	}

	return parseDiff(string(output))
}

// GetAllChanges gets all changes (both unstaged and staged)
func GetAllChanges() (*DiffResult, error) {
	// Get unstaged changes
	unstaged, err := GetUnstagedDiff()
	if err != nil {
		return nil, fmt.Errorf("failed to get unstaged changes: %w", err)
	}

	// Get staged changes
	staged, err := GetStagedDiff()
	if err != nil {
		return nil, fmt.Errorf("failed to get staged changes: %w", err)
	}

	// Combine both results
	result := &DiffResult{
		Changes: append(unstaged.Changes, staged.Changes...),
	}

	return result, nil
}

// parseDiff parses unified diff format and extracts changed lines
func parseDiff(diffText string) (*DiffResult, error) {
	result := &DiffResult{
		Changes: []ChangedLine{},
	}

	scanner := bufio.NewScanner(strings.NewReader(diffText))
	var currentFile string
	var currentLineNum int

	// Regex patterns for diff parsing
	filePattern := regexp.MustCompile(`^\+\+\+ b/(.+)$`)
	hunkPattern := regexp.MustCompile(`^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@`)

	for scanner.Scan() {
		line := scanner.Text()

		// Check for file marker
		if matches := filePattern.FindStringSubmatch(line); matches != nil {
			currentFile = matches[1]
			continue
		}

		// Check for hunk header (gives us line number)
		if matches := hunkPattern.FindStringSubmatch(line); matches != nil {
			lineNum, err := strconv.Atoi(matches[1])
			if err != nil {
				continue
			}
			currentLineNum = lineNum
			continue
		}

		// Process added/modified lines
		if strings.HasPrefix(line, "+") && !strings.HasPrefix(line, "+++") {
			// Added line
			content := strings.TrimPrefix(line, "+")
			result.Changes = append(result.Changes, ChangedLine{
				File:       currentFile,
				LineNumber: currentLineNum,
				Content:    content,
				ChangeType: "+",
			})
			currentLineNum++
		} else if strings.HasPrefix(line, "-") && !strings.HasPrefix(line, "---") {
			// Removed line - we don't check these for new violations
			continue
		} else if !strings.HasPrefix(line, "@") && !strings.HasPrefix(line, "\\") {
			// Context line (no +/-)
			currentLineNum++
		}
	}

	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("error parsing diff: %w", err)
	}

	return result, nil
}
