package services

import (
	"context"
)

// =============================================
// STATISTICS METHODS
// =============================================

// ClassStatistics holds statistics for a class
type ClassStatistics struct {
	ClassID           string             `json:"class_id"`
	ClassName         string             `json:"class_name"`
	StudentCount      int                `json:"student_count"`
	ClassAverage      float64            `json:"class_average"`
	GradeDistribution map[string]int     `json:"grade_distribution"`
	BestGrade         float64            `json:"best_grade"`
	WorstGrade        float64            `json:"worst_grade"`
	PassRate          float64            `json:"pass_rate"`
	StudentsAtRisk    int                `json:"students_at_risk"`
	SubjectAverages   map[string]float64 `json:"subject_averages"`
}

// SubjectStatistics holds statistics for a subject within a class
type SubjectStatistics struct {
	ClassID           string          `json:"class_id"`
	SubjectID         string          `json:"subject_id"`
	SubjectName       string          `json:"subject_name"`
	StudentCount      int             `json:"student_count"`
	Average           float64         `json:"average"`
	Median            float64         `json:"median"`
	GradeDistribution map[string]int  `json:"grade_distribution"`
	BestGrade         float64         `json:"best_grade"`
	WorstGrade        float64         `json:"worst_grade"`
	PassRate          float64         `json:"pass_rate"`
	ExamAverages      []ExamAverage   `json:"exam_averages"`
}

// ExamAverage holds average for a single exam
type ExamAverage struct {
	ExamID   string  `json:"exam_id"`
	Title    string  `json:"title"`
	Average  float64 `json:"average"`
	ExamDate string  `json:"exam_date"`
}

// StudentStatistics holds statistics for a single student
type StudentStatistics struct {
	StudentID       string             `json:"student_id"`
	StudentName     string             `json:"student_name"`
	OverallAverage  float64            `json:"overall_average"`
	SubjectGrades   map[string]float64 `json:"subject_grades"`
	Trend           string             `json:"trend"` // "improving", "stable", "declining"
	AbsenceDays     int                `json:"absence_days"`
	ExamsCompleted  int                `json:"exams_completed"`
	StrongestSubject string            `json:"strongest_subject"`
	WeakestSubject   string            `json:"weakest_subject"`
}

// Notenspiegel represents grade distribution
type Notenspiegel struct {
	ClassID      string         `json:"class_id"`
	SubjectID    string         `json:"subject_id,omitempty"`
	ExamID       string         `json:"exam_id,omitempty"`
	Title        string         `json:"title"`
	Distribution map[string]int `json:"distribution"`
	Total        int            `json:"total"`
	Average      float64        `json:"average"`
	PassRate     float64        `json:"pass_rate"`
}

// GetClassStatistics returns statistics for a class
func (s *GradeService) GetClassStatistics(ctx context.Context, classID string, semester int) (*ClassStatistics, error) {
	stats := &ClassStatistics{
		ClassID:           classID,
		GradeDistribution: make(map[string]int),
		SubjectAverages:   make(map[string]float64),
	}

	// Initialize grade distribution
	for i := 1; i <= 6; i++ {
		stats.GradeDistribution[string('0'+rune(i))] = 0
	}

	// Get class info and student count
	err := s.db.QueryRow(ctx, `
		SELECT c.name, COUNT(s.id)
		FROM classes c
		LEFT JOIN students s ON s.class_id = c.id
		WHERE c.id = $1
		GROUP BY c.name
	`, classID).Scan(&stats.ClassName, &stats.StudentCount)
	if err != nil {
		return nil, err
	}

	// Build semester condition
	semesterCond := ""
	if semester > 0 {
		semesterCond = " AND go.semester = " + string('0'+rune(semester))
	}

	// Get overall statistics from grade_overview
	var avgGrade, bestGrade, worstGrade float64
	var totalPassed, totalStudents int
	err = s.db.QueryRow(ctx, `
		SELECT
			COALESCE(AVG(go.final_grade), 0),
			COALESCE(MIN(go.final_grade), 0),
			COALESCE(MAX(go.final_grade), 0),
			COUNT(CASE WHEN go.final_grade <= 4.0 THEN 1 END),
			COUNT(go.id)
		FROM grade_overview go
		JOIN students s ON s.id = go.student_id
		WHERE s.class_id = $1 AND go.final_grade IS NOT NULL`+semesterCond+`
	`, classID).Scan(&avgGrade, &bestGrade, &worstGrade, &totalPassed, &totalStudents)

	if err == nil {
		stats.ClassAverage = avgGrade
		stats.BestGrade = bestGrade
		stats.WorstGrade = worstGrade
		if totalStudents > 0 {
			stats.PassRate = float64(totalPassed) / float64(totalStudents) * 100
		}
	}

	// Get grade distribution
	rows, err := s.db.Query(ctx, `
		SELECT FLOOR(go.final_grade) as grade_bucket, COUNT(*) as count
		FROM grade_overview go
		JOIN students s ON s.id = go.student_id
		WHERE s.class_id = $1 AND go.final_grade IS NOT NULL`+semesterCond+`
		GROUP BY FLOOR(go.final_grade)
	`, classID)
	if err == nil {
		defer rows.Close()
		for rows.Next() {
			var bucket int
			var count int
			if err := rows.Scan(&bucket, &count); err == nil && bucket >= 1 && bucket <= 6 {
				stats.GradeDistribution[string('0'+rune(bucket))] = count
			}
		}
	}

	// Count students at risk
	s.db.QueryRow(ctx, `
		SELECT COUNT(DISTINCT s.id)
		FROM students s
		JOIN grade_overview go ON go.student_id = s.id
		WHERE s.class_id = $1 AND go.final_grade >= 4.5
	`, classID).Scan(&stats.StudentsAtRisk)

	// Get subject averages
	subjectRows, err := s.db.Query(ctx, `
		SELECT sub.name, AVG(go.final_grade)
		FROM grade_overview go
		JOIN subjects sub ON sub.id = go.subject_id
		JOIN students s ON s.id = go.student_id
		WHERE s.class_id = $1 AND go.final_grade IS NOT NULL`+semesterCond+`
		GROUP BY sub.name
	`, classID)
	if err == nil {
		defer subjectRows.Close()
		for subjectRows.Next() {
			var name string
			var avg float64
			if err := subjectRows.Scan(&name, &avg); err == nil {
				stats.SubjectAverages[name] = avg
			}
		}
	}

	return stats, nil
}

// GetSubjectStatistics returns statistics for a specific subject in a class
func (s *GradeService) GetSubjectStatistics(ctx context.Context, classID, subjectID string, semester int) (*SubjectStatistics, error) {
	stats := &SubjectStatistics{
		ClassID:           classID,
		SubjectID:         subjectID,
		GradeDistribution: make(map[string]int),
		ExamAverages:      []ExamAverage{},
	}

	// Initialize grade distribution
	for i := 1; i <= 6; i++ {
		stats.GradeDistribution[string('0'+rune(i))] = 0
	}

	// Get subject name and basic stats
	semesterCond := ""
	if semester > 0 {
		semesterCond = " AND go.semester = " + string('0'+rune(semester))
	}

	err := s.db.QueryRow(ctx, `
		SELECT
			sub.name,
			COUNT(DISTINCT s.id),
			COALESCE(AVG(go.final_grade), 0),
			COALESCE(MIN(go.final_grade), 0),
			COALESCE(MAX(go.final_grade), 0),
			COUNT(CASE WHEN go.final_grade <= 4.0 THEN 1 END),
			COUNT(go.id)
		FROM grade_overview go
		JOIN subjects sub ON sub.id = go.subject_id
		JOIN students s ON s.id = go.student_id
		WHERE s.class_id = $1 AND go.subject_id = $2 AND go.final_grade IS NOT NULL`+semesterCond+`
		GROUP BY sub.name
	`, classID, subjectID).Scan(
		&stats.SubjectName, &stats.StudentCount, &stats.Average,
		&stats.BestGrade, &stats.WorstGrade,
		new(int), new(int), // We'll calculate pass rate separately
	)
	if err != nil {
		return nil, err
	}

	// Get grade distribution
	rows, err := s.db.Query(ctx, `
		SELECT FLOOR(go.final_grade), COUNT(*)
		FROM grade_overview go
		JOIN students s ON s.id = go.student_id
		WHERE s.class_id = $1 AND go.subject_id = $2 AND go.final_grade IS NOT NULL`+semesterCond+`
		GROUP BY FLOOR(go.final_grade)
	`, classID, subjectID)
	if err == nil {
		defer rows.Close()
		var passed, total int
		for rows.Next() {
			var bucket, count int
			if err := rows.Scan(&bucket, &count); err == nil && bucket >= 1 && bucket <= 6 {
				stats.GradeDistribution[string('0'+rune(bucket))] = count
				total += count
				if bucket <= 4 {
					passed += count
				}
			}
		}
		if total > 0 {
			stats.PassRate = float64(passed) / float64(total) * 100
		}
	}

	// Get exam averages
	examRows, err := s.db.Query(ctx, `
		SELECT e.id, e.title, AVG(er.grade), COALESCE(e.exam_date::text, '')
		FROM exams e
		JOIN exam_results er ON er.exam_id = e.id
		JOIN students s ON er.student_id = s.id
		WHERE s.class_id = $1 AND e.subject_id = $2 AND er.grade IS NOT NULL
		GROUP BY e.id, e.title, e.exam_date
		ORDER BY e.exam_date DESC NULLS LAST
	`, classID, subjectID)
	if err == nil {
		defer examRows.Close()
		for examRows.Next() {
			var ea ExamAverage
			if err := examRows.Scan(&ea.ExamID, &ea.Title, &ea.Average, &ea.ExamDate); err == nil {
				stats.ExamAverages = append(stats.ExamAverages, ea)
			}
		}
	}

	return stats, nil
}

// GetStudentStatistics returns statistics for a specific student
func (s *GradeService) GetStudentStatistics(ctx context.Context, studentID string) (*StudentStatistics, error) {
	stats := &StudentStatistics{
		StudentID:     studentID,
		SubjectGrades: make(map[string]float64),
	}

	// Get student name
	err := s.db.QueryRow(ctx, `
		SELECT CONCAT(first_name, ' ', last_name) FROM students WHERE id = $1
	`, studentID).Scan(&stats.StudentName)
	if err != nil {
		return nil, err
	}

	// Get subject grades and calculate overall average
	var totalGrade, numSubjects float64
	var bestGrade float64 = 6
	var worstGrade float64 = 1
	var bestSubject, worstSubject string

	rows, err := s.db.Query(ctx, `
		SELECT sub.name, go.final_grade
		FROM grade_overview go
		JOIN subjects sub ON sub.id = go.subject_id
		WHERE go.student_id = $1 AND go.final_grade IS NOT NULL
		ORDER BY sub.name
	`, studentID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var name string
		var grade float64
		if err := rows.Scan(&name, &grade); err == nil {
			stats.SubjectGrades[name] = grade
			totalGrade += grade
			numSubjects++
			if grade < bestGrade {
				bestGrade = grade
				bestSubject = name
			}
			if grade > worstGrade {
				worstGrade = grade
				worstSubject = name
			}
		}
	}

	if numSubjects > 0 {
		stats.OverallAverage = totalGrade / numSubjects
		stats.StrongestSubject = bestSubject
		stats.WeakestSubject = worstSubject
	}

	// Count exams completed
	s.db.QueryRow(ctx, `
		SELECT COUNT(*) FROM exam_results WHERE student_id = $1 AND grade IS NOT NULL
	`, studentID).Scan(&stats.ExamsCompleted)

	// Count absence days
	s.db.QueryRow(ctx, `
		SELECT COALESCE(SUM(periods), 0) FROM attendance
		WHERE student_id = $1 AND status IN ('absent_excused', 'absent_unexcused')
	`, studentID).Scan(&stats.AbsenceDays)

	// Determine trend (simplified - compare first and last exam grades)
	stats.Trend = "stable"
	var firstGrade, lastGrade float64
	s.db.QueryRow(ctx, `
		SELECT grade FROM exam_results
		WHERE student_id = $1 AND grade IS NOT NULL
		ORDER BY created_at ASC LIMIT 1
	`, studentID).Scan(&firstGrade)
	s.db.QueryRow(ctx, `
		SELECT grade FROM exam_results
		WHERE student_id = $1 AND grade IS NOT NULL
		ORDER BY created_at DESC LIMIT 1
	`, studentID).Scan(&lastGrade)

	if lastGrade < firstGrade-0.5 {
		stats.Trend = "improving"
	} else if lastGrade > firstGrade+0.5 {
		stats.Trend = "declining"
	}

	return stats, nil
}

// GetNotenspiegel returns grade distribution (Notenspiegel)
func (s *GradeService) GetNotenspiegel(ctx context.Context, classID, subjectID, examID string, semester int) (*Notenspiegel, error) {
	ns := &Notenspiegel{
		ClassID:      classID,
		SubjectID:    subjectID,
		ExamID:       examID,
		Distribution: make(map[string]int),
	}

	// Initialize distribution
	for i := 1; i <= 6; i++ {
		ns.Distribution[string('0'+rune(i))] = 0
	}

	var query string
	var args []interface{}

	if examID != "" {
		// Notenspiegel for specific exam
		query = `
			SELECT e.title, FLOOR(er.grade), COUNT(*), AVG(er.grade)
			FROM exam_results er
			JOIN exams e ON e.id = er.exam_id
			WHERE er.exam_id = $1 AND er.grade IS NOT NULL
			GROUP BY e.title, FLOOR(er.grade)
		`
		args = []interface{}{examID}
	} else if subjectID != "" {
		// Notenspiegel for subject in class
		semesterCond := ""
		if semester > 0 {
			semesterCond = " AND go.semester = " + string('0'+rune(semester))
		}
		query = `
			SELECT sub.name, FLOOR(go.final_grade), COUNT(*), AVG(go.final_grade)
			FROM grade_overview go
			JOIN subjects sub ON sub.id = go.subject_id
			JOIN students s ON s.id = go.student_id
			WHERE s.class_id = $1 AND go.subject_id = $2 AND go.final_grade IS NOT NULL` + semesterCond + `
			GROUP BY sub.name, FLOOR(go.final_grade)
		`
		args = []interface{}{classID, subjectID}
	} else {
		// Notenspiegel for entire class
		semesterCond := ""
		if semester > 0 {
			semesterCond = " AND go.semester = " + string('0'+rune(semester))
		}
		query = `
			SELECT c.name, FLOOR(go.final_grade), COUNT(*), AVG(go.final_grade)
			FROM grade_overview go
			JOIN students s ON s.id = go.student_id
			JOIN classes c ON c.id = s.class_id
			WHERE s.class_id = $1 AND go.final_grade IS NOT NULL` + semesterCond + `
			GROUP BY c.name, FLOOR(go.final_grade)
		`
		args = []interface{}{classID}
	}

	rows, err := s.db.Query(ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var passed int
	for rows.Next() {
		var title string
		var bucket, count int
		var avg float64
		if err := rows.Scan(&title, &bucket, &count, &avg); err == nil {
			ns.Title = title
			if bucket >= 1 && bucket <= 6 {
				ns.Distribution[string('0'+rune(bucket))] = count
				ns.Total += count
				if bucket <= 4 {
					passed += count
				}
			}
			ns.Average = avg
		}
	}

	if ns.Total > 0 {
		ns.PassRate = float64(passed) / float64(ns.Total) * 100
	}

	return ns, nil
}
