package quality

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

// Scorer calculates quality scores and generates scorecards
type Scorer struct {
	targetScore int
}

// NewScorer creates a new scorer with the given target score
func NewScorer(targetScore int) *Scorer {
	if targetScore <= 0 {
		targetScore = 95 // Default target
	}
	return &Scorer{
		targetScore: targetScore,
	}
}

// CalculateScore calculates the quality score from findings
func (s *Scorer) CalculateScore(findings []Finding) (int, int) {
	totalScore := 0
	strictScore := 0

	for _, finding := range findings {
		weight := int(finding.Severity)
		score := finding.Score * weight
		totalScore += score

		// Strict score: ONLY includes truly unresolved issues
		// Excludes: fixed, false_positive, ignored, wontfix (if justified)
		if s.isStrictlyRelevant(finding) {
			// Apply strict multiplier for severity
			strictMultiplier := s.getStrictMultiplier(finding)
			strictScore += score * strictMultiplier
		}
	}

	return totalScore, strictScore
}

// GenerateScorecard creates a scorecard from scan results
func (s *Scorer) GenerateScorecard(findings []Finding, lastScan time.Time) *Scorecard {
	totalScore, strictScore := s.CalculateScore(findings)

	// Group findings by type and tier
	findingsByType := make(map[string]int)
	findingsByTier := make(map[Severity]int)
	statusByType := make(map[string]int)

	for _, finding := range findings {
		findingsByType[finding.Type]++
		findingsByTier[finding.Severity]++
		statusByType[string(finding.Status)]++
	}

	return &Scorecard{
		TotalScore:     totalScore,
		StrictScore:    strictScore,
		TargetScore:    s.targetScore,
		FindingsByType: findingsByType,
		FindingsByTier: findingsByTier,
		StatusByType:   statusByType,
		LastScan:       lastScan,
	}
}

// isStrictlyRelevant determines if a finding should count in strict scoring
func (s *Scorer) isStrictlyRelevant(finding Finding) bool {
	switch finding.Status {
	case StatusOpen:
		return true
	case StatusFixed:
		return false // Already resolved
	case StatusFalsePositive:
		return false // Not a real issue
	case StatusIgnored:
		return false // Explicitly ignored
	case StatusWontfix:
		// Only count wontfix if it's not justified with valid reasons
		return !s.isJustifiedWontfix(finding)
	default:
		return true
	}
}

// isJustifiedWontfix checks if wontfix has valid justification
func (s *Scorer) isJustifiedWontfix(finding Finding) bool {
	if finding.Metadata == nil {
		return false
	}

	note, exists := finding.Metadata["resolution_note"]
	if !exists {
		return false
	}

	// Valid wontfix justifications
	validJustifications := []string{
		"legacy", "deprecated", "external", "third-party",
		"temporary", "placeholder", "documentation",
		"test-only", "example", "sample",
	}

	note = strings.ToLower(note)
	for _, justification := range validJustifications {
		if strings.Contains(note, justification) {
			return true
		}
	}

	return false
}

// getStrictMultiplier returns severity multiplier for strict scoring
func (s *Scorer) getStrictMultiplier(finding Finding) int {
	switch finding.Severity {
	case SeverityT1:
		return 1 // T1 issues are less critical
	case SeverityT2:
		return 2 // T2 issues are moderately important
	case SeverityT3:
		return 3 // T3 issues need attention
	case SeverityT4:
		return 5 // T4 issues are critical
	default:
		return 1
	}
}

// GetHealthGrade returns a health grade based on score
func (s *Scorer) GetHealthGrade(score int) string {
	percentage := s.getScorePercentage(score)

	switch {
	case percentage >= 90:
		return "A"
	case percentage >= 80:
		return "B"
	case percentage >= 70:
		return "C"
	case percentage >= 60:
		return "D"
	default:
		return "F"
	}
}

// getScorePercentage converts score to percentage (inverted - lower is better)
func (s *Scorer) getScorePercentage(score int) int {
	// Strict percentage calculation with multiple factors
	if score <= 0 {
		return 100
	}

	// Base calculation with stricter normalization
	var percentage int
	if score > 10000 {
		// Logarithmic scaling for very high scores
		percentage = 100 - int(float64(score-10000)/float64(score)*90)
	} else if score > 5000 {
		// Linear scaling for high scores
		percentage = 100 - (score * 100 / 20000)
	} else if score > 1000 {
		// Linear scaling for medium scores
		percentage = 100 - (score * 100 / 10000)
	} else {
		// Linear scaling for low scores
		percentage = 100 - (score * 100 / 2000)
	}

	if percentage < 0 {
		percentage = 0
	}
	return percentage
}

// GetStrictHealthMetrics returns comprehensive strict health metrics
func (s *Scorer) GetStrictHealthMetrics(findings []Finding) map[string]interface{} {
	total := len(findings)
	open := 0
	critical := 0
	high := 0
	medium := 0
	low := 0
	resolved := 0
	ignored := 0

	strictScore := 0
	totalScore := 0

	for _, finding := range findings {
		totalScore += finding.Score * int(finding.Severity)

		switch finding.Status {
		case StatusOpen:
			open++
			if s.isStrictlyRelevant(finding) {
				strictScore += finding.Score * int(finding.Severity) * s.getStrictMultiplier(finding)
			}
		case StatusFixed:
			resolved++
		case StatusIgnored, StatusFalsePositive:
			ignored++
		}

		switch finding.Severity {
		case SeverityT4:
			critical++
		case SeverityT3:
			high++
		case SeverityT2:
			medium++
		case SeverityT1:
			low++
		}
	}

	// Calculate strict percentages
	openPercentage := float64(open) / float64(total) * 100
	criticalPercentage := float64(critical) / float64(total) * 100
	resolutionRate := float64(resolved) / float64(total) * 100

	// Strict health score (0-100)
	healthScore := 100.0
	healthScore -= float64(openPercentage) * 0.5     // Penalty for open issues
	healthScore -= float64(criticalPercentage) * 2.0 // Higher penalty for critical
	healthScore -= float64(high) * 0.1               // Penalty for high severity
	healthScore += float64(resolutionRate) * 0.3     // Bonus for resolution rate

	if healthScore < 0 {
		healthScore = 0
	}
	if healthScore > 100 {
		healthScore = 100
	}

	return map[string]interface{}{
		"total_issues":        total,
		"open_issues":         open,
		"critical_issues":     critical,
		"high_issues":         high,
		"medium_issues":       medium,
		"low_issues":          low,
		"resolved_issues":     resolved,
		"ignored_issues":      ignored,
		"open_percentage":     openPercentage,
		"critical_percentage": criticalPercentage,
		"resolution_rate":     resolutionRate,
		"strict_score":        strictScore,
		"total_score":         totalScore,
		"health_score":        healthScore,
		"grade":               s.GetStrictGrade(healthScore),
	}
}

// GetStrictGrade returns grade based on strict health score
func (s *Scorer) GetStrictGrade(healthScore float64) string {
	switch {
	case healthScore >= 95:
		return "A+"
	case healthScore >= 90:
		return "A"
	case healthScore >= 85:
		return "A-"
	case healthScore >= 80:
		return "B+"
	case healthScore >= 75:
		return "B"
	case healthScore >= 70:
		return "B-"
	case healthScore >= 65:
		return "C+"
	case healthScore >= 60:
		return "C"
	case healthScore >= 55:
		return "C-"
	case healthScore >= 50:
		return "D+"
	case healthScore >= 45:
		return "D"
	case healthScore >= 40:
		return "D-"
	default:
		return "F"
	}
}

// FormatScorecard formats the scorecard for display
func (s *Scorer) FormatScorecard(card *Scorecard) string {
	grade := s.GetHealthGrade(card.StrictScore)
	percentage := s.getScorePercentage(card.StrictScore)

	output := fmt.Sprintf(`
🔍 STRICT Code Quality Scorecard
=======================================

📊 Overall Health: %s (%d%%)
🎯 Target Score: %d
⚡ Current Score: %d (strict: %d)

📈 Findings by Type:
`, grade, percentage, card.TargetScore, card.TotalScore, card.StrictScore)

	for ftype, count := range card.FindingsByType {
		output += fmt.Sprintf("  - %s: %d\n", ftype, count)
	}

	output += "\n🚨 Findings by Severity:\n"
	tierNames := map[Severity]string{
		SeverityT1: "T1 (Auto-fixable)",
		SeverityT2: "T2 (Quick manual)",
		SeverityT3: "T3 (Needs judgment)",
		SeverityT4: "T4 (Major refactor)",
	}

	for severity, count := range card.FindingsByTier {
		if name, ok := tierNames[severity]; ok {
			emoji := s.getSeverityEmoji(severity)
			output += fmt.Sprintf("  %s %s: %d\n", emoji, name, count)
		}
	}

	output += "\n📋 Status Breakdown:\n"
	for status, count := range card.StatusByType {
		output += fmt.Sprintf("  - %s: %d\n", status, count)
	}

	output += fmt.Sprintf("\n⏰ Last Scan: %s\n", card.LastScan.Format("2006-01-02 15:04:05"))

	return output
}

// getSeverityEmoji returns emoji for severity level
func (s *Scorer) getSeverityEmoji(severity Severity) string {
	switch severity {
	case SeverityT1:
		return "🟢"
	case SeverityT2:
		return "🟡"
	case SeverityT3:
		return "🟠"
	case SeverityT4:
		return "🔴"
	default:
		return "⚪"
	}
}

// FormatStrictScorecard formats comprehensive strict scorecard
func (s *Scorer) FormatStrictScorecard(findings []Finding, lastScan time.Time) string {
	metrics := s.GetStrictHealthMetrics(findings)

	output := fmt.Sprintf(`
🔬 COMPREHENSIVE STRICT ANALYSIS
=======================================

🎯 STRICT HEALTH SCORE: %.1f/100 (%s)
=======================================

📊 ISSUE BREAKDOWN:
  Total Issues: %v
  🔴 Critical (T4): %v (%.1f%%)
  🟠 High (T3): %v
  🟡 Medium (T2): %v
  🟢 Low (T1): %v

📈 STATUS ANALYSIS:
  ✅ Resolved: %v (%.1f%%)
  🔓 Open: %v (%.1f%%)
  ⏸️  Ignored: %v

⚖️  SCORING:
  Strict Score: %v
  Total Score: %v
  Health Multiplier: %.2fx

🎯 STRICT CRITERIA:
  ✓ Only unresolved issues counted
  ✓ Severity-weighted scoring (T1×1, T2×2, T3×3, T4×5)
  ✓ Justified wontfix excluded
  ✓ False positives ignored
  ✓ Resolution rate bonus applied

📅 Last Analysis: %s

🏆 RECOMMENDATIONS:
`,
		metrics["health_score"], metrics["grade"],
		metrics["total_issues"],
		metrics["critical_issues"], metrics["critical_percentage"],
		metrics["high_issues"],
		metrics["medium_issues"],
		metrics["low_issues"],
		metrics["resolved_issues"], metrics["resolution_rate"],
		metrics["open_issues"], metrics["open_percentage"],
		metrics["ignored_issues"],
		metrics["strict_score"], metrics["total_score"],
		float64(metrics["strict_score"].(int))/float64(metrics["total_score"].(int)),
		lastScan.Format("2006-01-02 15:04:05"))

	// Add recommendations based on metrics
	if metrics["critical_percentage"].(float64) > 5 {
		output += "  🚨 CRITICAL: Address T4 issues immediately\n"
	}
	if metrics["open_percentage"].(float64) > 70 {
		output += "  📈 HIGH DEBT: Focus on resolving open issues\n"
	}
	if metrics["resolution_rate"].(float64) < 20 {
		output += "  ⚡ LOW RESOLUTION: Increase fix rate\n"
	}
	if healthScore, ok := metrics["health_score"].(float64); ok && healthScore < 50 {
		output += "  ❌ POOR HEALTH: Major refactoring needed\n"
	} else if healthScore >= 80 {
		output += "  ✅ GOOD HEALTH: Maintain current practices\n"
	}

	return output
}

// GetNextPriority returns the next highest priority finding to fix
func (s *Scorer) GetNextPriority(findings []Finding) *Finding {
	if len(findings) == 0 {
		return nil
	}

	var highest *Finding
	highestWeight := 0

	for _, finding := range findings {
		if finding.Status != StatusOpen {
			continue
		}

		weight := int(finding.Severity) * finding.Score
		if weight > highestWeight {
			highestWeight = weight
			highest = &finding
		}
	}

	return highest
}

// GetFindingsByTier returns findings grouped by severity tier
func (s *Scorer) GetFindingsByTier(findings []Finding) map[Severity][]Finding {
	result := make(map[Severity][]Finding)

	for _, finding := range findings {
		if finding.Status == StatusOpen {
			result[finding.Severity] = append(result[finding.Severity], finding)
		}
	}

	return result
}

// GetProgressMetrics returns progress metrics for the scan
func (s *Scorer) GetProgressMetrics(findings []Finding) map[string]interface{} {
	total := len(findings)
	open := 0
	fixed := 0
	wontfix := 0

	for _, finding := range findings {
		switch finding.Status {
		case StatusOpen:
			open++
		case StatusFixed:
			fixed++
		case StatusWontfix:
			wontfix++
		}
	}

	return map[string]interface{}{
		"total":    total,
		"open":     open,
		"fixed":    fixed,
		"wontfix":  wontfix,
		"progress": float64(fixed) / float64(total) * 100,
	}
}
