package generator

import (
	"fmt"
	"time"
)

// BenchmarkResult contains statistics from puzzle generation benchmarking.
type BenchmarkResult struct {
	Difficulty        Difficulty
	TotalAttempts     int
	SuccessfulPuzzles int
	FailedPuzzles     int
	AverageTime       time.Duration
	MinTime           time.Duration
	MaxTime           time.Duration
	AverageRating     float64
	Ratings           []int
}

// BenchmarkGeneration tests puzzle generation performance for a given difficulty.
func BenchmarkGeneration(d Difficulty, attempts int) BenchmarkResult {
	result := BenchmarkResult{
		Difficulty:    d,
		TotalAttempts: attempts,
		MinTime:       time.Hour, // Start with a large value
		Ratings:       make([]int, 0, attempts),
	}

	var totalDuration time.Duration

	for i := 0; i < attempts; i++ {
		seed := fmt.Sprintf("benchmark-%d-%d", time.Now().UnixNano(), i)
		start := time.Now()

		puzzle, err := Generate(d, seed)
		elapsed := time.Since(start)

		if err != nil {
			result.FailedPuzzles++
			continue
		}

		result.SuccessfulPuzzles++
		totalDuration += elapsed

		if elapsed < result.MinTime {
			result.MinTime = elapsed
		}
		if elapsed > result.MaxTime {
			result.MaxTime = elapsed
		}

		rating := RatePuzzle(puzzle)
		result.Ratings = append(result.Ratings, rating)
	}

	if result.SuccessfulPuzzles > 0 {
		result.AverageTime = totalDuration / time.Duration(result.SuccessfulPuzzles)

		var totalRating int
		for _, r := range result.Ratings {
			totalRating += r
		}
		result.AverageRating = float64(totalRating) / float64(len(result.Ratings))
	}

	return result
}

// String returns a formatted string representation of the benchmark result.
func (br BenchmarkResult) String() string {
	successRate := float64(br.SuccessfulPuzzles) / float64(br.TotalAttempts) * 100

	return fmt.Sprintf(`Benchmark Results for %s:
  Total Attempts:     %d
  Successful:         %d (%.1f%%)
  Failed:             %d
  Average Time:       %v
  Min Time:           %v
  Max Time:           %v
  Average Rating:     %.1f/100
`,
		br.Difficulty.String(),
		br.TotalAttempts,
		br.SuccessfulPuzzles,
		successRate,
		br.FailedPuzzles,
		br.AverageTime,
		br.MinTime,
		br.MaxTime,
		br.AverageRating,
	)
}

// CompareGenerationMethods compares standard vs symmetric generation.
func CompareGenerationMethods(d Difficulty, attempts int) (standard, symmetric BenchmarkResult) {
	// Benchmark standard generation
	standard = BenchmarkGeneration(d, attempts)

	// Benchmark symmetric generation
	symmetric = BenchmarkResult{
		Difficulty:    d,
		TotalAttempts: attempts,
		MinTime:       time.Hour,
		Ratings:       make([]int, 0, attempts),
	}

	var totalDuration time.Duration

	for i := 0; i < attempts; i++ {
		seed := fmt.Sprintf("symmetric-benchmark-%d-%d", time.Now().UnixNano(), i)
		start := time.Now()

		puzzle, err := GenerateWithSymmetry(d, seed, SymmetryRotational180)
		elapsed := time.Since(start)

		if err != nil {
			symmetric.FailedPuzzles++
			continue
		}

		symmetric.SuccessfulPuzzles++
		totalDuration += elapsed

		if elapsed < symmetric.MinTime {
			symmetric.MinTime = elapsed
		}
		if elapsed > symmetric.MaxTime {
			symmetric.MaxTime = elapsed
		}

		rating := RatePuzzle(puzzle)
		symmetric.Ratings = append(symmetric.Ratings, rating)
	}

	if symmetric.SuccessfulPuzzles > 0 {
		symmetric.AverageTime = totalDuration / time.Duration(symmetric.SuccessfulPuzzles)

		var totalRating int
		for _, r := range symmetric.Ratings {
			totalRating += r
		}
		symmetric.AverageRating = float64(totalRating) / float64(len(symmetric.Ratings))
	}

	return standard, symmetric
}

// PuzzleStatistics provides detailed statistics about a generated puzzle.
type PuzzleStatistics struct {
	Grid               Grid
	Analysis           PuzzleAnalysis
	Rating             int
	EstimatedSolveTime time.Duration
	ComplexityScore    float64
}

// GetPuzzleStatistics performs comprehensive analysis on a puzzle.
func GetPuzzleStatistics(g Grid) PuzzleStatistics {
	analysis := g.Analyze()
	rating := RatePuzzle(g)

	// Estimate solve time based on difficulty (rough approximation)
	var estimatedTime time.Duration
	switch analysis.EstimatedDifficulty {
	case Easy:
		estimatedTime = 3 * time.Minute
	case Normal:
		estimatedTime = 8 * time.Minute
	case Hard:
		estimatedTime = 15 * time.Minute
	case Expert:
		estimatedTime = 25 * time.Minute
	case Lunatic:
		estimatedTime = 45 * time.Minute
	}

	// Calculate complexity score (0-1)
	complexityScore := float64(rating) / 100.0

	return PuzzleStatistics{
		Grid:               g,
		Analysis:           analysis,
		Rating:             rating,
		EstimatedSolveTime: estimatedTime,
		ComplexityScore:    complexityScore,
	}
}

// String returns a formatted string of puzzle statistics.
func (ps PuzzleStatistics) String() string {
	return fmt.Sprintf(`Puzzle Statistics:
  Difficulty:         %s
  Rating:             %d/100
  Filled Cells:       %d
  Empty Cells:        %d
  Min Candidates:     %d
  Max Candidates:     %d
  Avg Candidates:     %.2f
  Unique Solution:    %v
  Symmetry:           %s
  Techniques:         %v
  Complexity Score:   %.2f
  Est. Solve Time:    %v
`,
		ps.Analysis.EstimatedDifficulty.String(),
		ps.Rating,
		ps.Analysis.FilledCells,
		ps.Analysis.EmptyCells,
		ps.Analysis.MinCandidates,
		ps.Analysis.MaxCandidates,
		ps.Analysis.AvgCandidates,
		ps.Analysis.HasUniqueSolution,
		ps.Analysis.SymmetryType.String(),
		ps.Analysis.SolvingTechniques,
		ps.ComplexityScore,
		ps.EstimatedSolveTime,
	)
}
