package checker

import (
	"context"
	"fmt"
	"strings"

	"github.com/tanagram/cli/git"
	"github.com/tanagram/cli/parser"
)

// CheckChangesWithLLM checks code changes against ALL policies using LLM
func CheckChangesWithLLM(ctx context.Context, changes []git.ChangedLine, policies []parser.Policy) []Violation {
	if len(policies) == 0 {
		return []Violation{}
	}

	// Convert policies slice to map for O(1) lookup
	policyMap := buildPolicyMap(policies)

	// Group changes by file for efficient checking
	changesByFile := groupChangesByFile(changes)

	var allViolations []Violation
	for file, fileChanges := range changesByFile {
		violations := checkFileWithLLM(ctx, file, fileChanges, policies, policyMap)
		allViolations = append(allViolations, violations...)
	}

	return allViolations
}

// buildPolicyMap converts a slice of policies to a map keyed by policy name for O(1) lookup
func buildPolicyMap(policies []parser.Policy) map[string]parser.Policy {
	policyMap := make(map[string]parser.Policy, len(policies))
	for _, policy := range policies {
		policyMap[policy.Name] = policy
	}
	return policyMap
}

// groupChangesByFile organizes changed lines by their file path
func groupChangesByFile(changes []git.ChangedLine) map[string][]git.ChangedLine {
	grouped := make(map[string][]git.ChangedLine)
	for _, change := range changes {
		grouped[change.File] = append(grouped[change.File], change)
	}
	return grouped
}

// checkFileWithLLM checks a single file's changes using the LLM
func checkFileWithLLM(ctx context.Context, file string, changes []git.ChangedLine, policies []parser.Policy, policyMap map[string]parser.Policy) []Violation {
	// Format changes for LLM
	codeChanges := formatChangesForLLM(changes)

	// Call LLM to check violations
	checks, err := CheckViolations(ctx, file, codeChanges, policies)
	if err != nil {
		// Log error but don't fail the whole check
		fmt.Printf("Warning: LLM check failed for %s: %v\n", file, err)
		return []Violation{}
	}

	// Convert LLM checks to Violations
	var violations []Violation
	for _, check := range checks {
		if !check.Violated {
			continue
		}

		// Find the policy details using O(1) map lookup
		policy, found := policyMap[check.PolicyName]
		if !found {
			continue
		}

		// Use first changed line as the location (LLM doesn't provide specific line numbers yet)
		firstLine := changes[0]

		violations = append(violations, Violation{
			File:       file,
			LineNumber: firstLine.LineNumber,
			PolicyName: policy.Name,
			Message:    check.Reason,
			Code:       formatChangesForDisplay(changes),
		})
	}

	return violations
}

// formatChangesForLLM creates a readable representation of code changes for the LLM
func formatChangesForLLM(changes []git.ChangedLine) string {
	var builder strings.Builder
	for _, change := range changes {
		builder.WriteString(fmt.Sprintf("Line %d: %s\n", change.LineNumber, change.Content))
	}
	return builder.String()
}

// formatChangesForDisplay creates a compact display of changed lines
func formatChangesForDisplay(changes []git.ChangedLine) string {
	// Just return the first line's content, trimmed
	// Line numbers will be shown in the formatted output
	if len(changes) > 0 {
		return strings.TrimSpace(changes[0].Content)
	}
	return ""
}
