package services

import (
	"testing"
	"time"

	"github.com/breakpilot/consent-service/internal/models"

	"github.com/google/uuid"
)

// TestValidateAttendanceRecord tests attendance record validation
func TestValidateAttendanceRecord(t *testing.T) {
	slotID := uuid.New()

	tests := []struct {
		name        string
		record      models.AttendanceRecord
		expectValid bool
	}{
		{
			name: "valid present record",
			record: models.AttendanceRecord{
				StudentID:  uuid.New(),
				SlotID:     slotID,
				Date:       time.Now(),
				Status:     models.AttendancePresent,
				RecordedBy: uuid.New(),
			},
			expectValid: true,
		},
		{
			name: "valid absent record",
			record: models.AttendanceRecord{
				StudentID:  uuid.New(),
				SlotID:     slotID,
				Date:       time.Now(),
				Status:     models.AttendanceAbsent,
				RecordedBy: uuid.New(),
			},
			expectValid: true,
		},
		{
			name: "valid late record",
			record: models.AttendanceRecord{
				StudentID:  uuid.New(),
				SlotID:     slotID,
				Date:       time.Now(),
				Status:     models.AttendanceLate,
				RecordedBy: uuid.New(),
			},
			expectValid: true,
		},
		{
			name: "missing student ID",
			record: models.AttendanceRecord{
				StudentID:  uuid.Nil,
				SlotID:     slotID,
				Date:       time.Now(),
				Status:     models.AttendancePresent,
				RecordedBy: uuid.New(),
			},
			expectValid: false,
		},
		{
			name: "invalid status",
			record: models.AttendanceRecord{
				StudentID:  uuid.New(),
				SlotID:     slotID,
				Date:       time.Now(),
				Status:     "invalid_status",
				RecordedBy: uuid.New(),
			},
			expectValid: false,
		},
		{
			name: "future date",
			record: models.AttendanceRecord{
				StudentID:  uuid.New(),
				SlotID:     slotID,
				Date:       time.Now().AddDate(0, 0, 7),
				Status:     models.AttendancePresent,
				RecordedBy: uuid.New(),
			},
			expectValid: false,
		},
		{
			name: "missing slot ID",
			record: models.AttendanceRecord{
				StudentID:  uuid.New(),
				SlotID:     uuid.Nil,
				Date:       time.Now(),
				Status:     models.AttendancePresent,
				RecordedBy: uuid.New(),
			},
			expectValid: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			isValid := validateAttendanceRecord(tt.record)
			if isValid != tt.expectValid {
				t.Errorf("expected valid=%v, got valid=%v", tt.expectValid, isValid)
			}
		})
	}
}

// validateAttendanceRecord validates an attendance record
func validateAttendanceRecord(record models.AttendanceRecord) bool {
	if record.StudentID == uuid.Nil {
		return false
	}
	if record.SlotID == uuid.Nil {
		return false
	}
	if record.RecordedBy == uuid.Nil {
		return false
	}
	if record.Date.After(time.Now().AddDate(0, 0, 1)) {
		return false
	}

	// Validate status
	validStatuses := map[string]bool{
		models.AttendancePresent:         true,
		models.AttendanceAbsent:          true,
		models.AttendanceAbsentExcused:   true,
		models.AttendanceAbsentUnexcused: true,
		models.AttendanceLate:            true,
		models.AttendanceLateExcused:     true,
		models.AttendancePending:         true,
	}

	if !validStatuses[record.Status] {
		return false
	}

	return true
}

// TestValidateAbsenceReport tests absence report validation
func TestValidateAbsenceReport(t *testing.T) {
	now := time.Now()
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)
	reason := "Krankheit"
	medicalReason := "Arzttermin"

	tests := []struct {
		name        string
		report      models.AbsenceReport
		expectValid bool
	}{
		{
			name: "valid single day absence",
			report: models.AbsenceReport{
				StudentID:      uuid.New(),
				ReportedBy:     uuid.New(),
				StartDate:      today,
				EndDate:        today,
				Reason:         &reason,
				ReasonCategory: "illness",
				Status:         "reported",
			},
			expectValid: true,
		},
		{
			name: "valid multi-day absence",
			report: models.AbsenceReport{
				StudentID:      uuid.New(),
				ReportedBy:     uuid.New(),
				StartDate:      today,
				EndDate:        today.AddDate(0, 0, 3),
				Reason:         &medicalReason,
				ReasonCategory: "appointment",
				Status:         "reported",
			},
			expectValid: true,
		},
		{
			name: "end before start",
			report: models.AbsenceReport{
				StudentID:      uuid.New(),
				ReportedBy:     uuid.New(),
				StartDate:      today.AddDate(0, 0, 3),
				EndDate:        today,
				Reason:         &reason,
				ReasonCategory: "illness",
				Status:         "reported",
			},
			expectValid: false,
		},
		{
			name: "missing reason category",
			report: models.AbsenceReport{
				StudentID:      uuid.New(),
				ReportedBy:     uuid.New(),
				StartDate:      today,
				EndDate:        today,
				Reason:         &reason,
				ReasonCategory: "",
				Status:         "reported",
			},
			expectValid: false,
		},
		{
			name: "invalid reason category",
			report: models.AbsenceReport{
				StudentID:      uuid.New(),
				ReportedBy:     uuid.New(),
				StartDate:      today,
				EndDate:        today,
				Reason:         &reason,
				ReasonCategory: "invalid_type",
				Status:         "reported",
			},
			expectValid: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			isValid := validateAbsenceReport(tt.report)
			if isValid != tt.expectValid {
				t.Errorf("expected valid=%v, got valid=%v", tt.expectValid, isValid)
			}
		})
	}
}

// validateAbsenceReport validates an absence report
func validateAbsenceReport(report models.AbsenceReport) bool {
	if report.StudentID == uuid.Nil {
		return false
	}
	if report.ReportedBy == uuid.Nil {
		return false
	}
	if report.EndDate.Before(report.StartDate) {
		return false
	}
	if report.ReasonCategory == "" {
		return false
	}

	// Validate reason category
	validCategories := map[string]bool{
		"illness":     true,
		"appointment": true,
		"family":      true,
		"other":       true,
	}

	if !validCategories[report.ReasonCategory] {
		return false
	}

	return true
}

// TestCalculateAttendanceStats tests attendance statistics calculation
func TestCalculateAttendanceStats(t *testing.T) {
	tests := []struct {
		name            string
		records         []models.AttendanceRecord
		expectedPresent int
		expectedAbsent  int
		expectedLate    int
	}{
		{
			name: "all present",
			records: []models.AttendanceRecord{
				{Status: models.AttendancePresent},
				{Status: models.AttendancePresent},
				{Status: models.AttendancePresent},
			},
			expectedPresent: 3,
			expectedAbsent:  0,
			expectedLate:    0,
		},
		{
			name: "mixed attendance",
			records: []models.AttendanceRecord{
				{Status: models.AttendancePresent},
				{Status: models.AttendanceAbsent},
				{Status: models.AttendanceLate},
				{Status: models.AttendancePresent},
				{Status: models.AttendanceAbsentExcused},
			},
			expectedPresent: 2,
			expectedAbsent:  2,
			expectedLate:    1,
		},
		{
			name:            "empty records",
			records:         []models.AttendanceRecord{},
			expectedPresent: 0,
			expectedAbsent:  0,
			expectedLate:    0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			present, absent, late := calculateAttendanceStats(tt.records)
			if present != tt.expectedPresent {
				t.Errorf("expected present=%d, got present=%d", tt.expectedPresent, present)
			}
			if absent != tt.expectedAbsent {
				t.Errorf("expected absent=%d, got absent=%d", tt.expectedAbsent, absent)
			}
			if late != tt.expectedLate {
				t.Errorf("expected late=%d, got late=%d", tt.expectedLate, late)
			}
		})
	}
}

// calculateAttendanceStats calculates attendance statistics
func calculateAttendanceStats(records []models.AttendanceRecord) (present, absent, late int) {
	for _, r := range records {
		switch r.Status {
		case models.AttendancePresent:
			present++
		case models.AttendanceAbsent, models.AttendanceAbsentExcused, models.AttendanceAbsentUnexcused:
			absent++
		case models.AttendanceLate, models.AttendanceLateExcused:
			late++
		}
	}
	return
}

// TestAttendanceRateCalculation tests attendance rate percentage calculation
func TestAttendanceRateCalculation(t *testing.T) {
	tests := []struct {
		name         string
		present      int
		total        int
		expectedRate float64
	}{
		{
			name:         "100% attendance",
			present:      26,
			total:        26,
			expectedRate: 100.0,
		},
		{
			name:         "92.3% attendance",
			present:      24,
			total:        26,
			expectedRate: 92.31,
		},
		{
			name:         "0% attendance",
			present:      0,
			total:        26,
			expectedRate: 0.0,
		},
		{
			name:         "empty class",
			present:      0,
			total:        0,
			expectedRate: 0.0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rate := calculateAttendanceRate(tt.present, tt.total)
			// Allow small floating point differences
			if rate < tt.expectedRate-0.1 || rate > tt.expectedRate+0.1 {
				t.Errorf("expected rate=%.2f, got rate=%.2f", tt.expectedRate, rate)
			}
		})
	}
}

// calculateAttendanceRate calculates attendance rate as percentage
func calculateAttendanceRate(present, total int) float64 {
	if total == 0 {
		return 0.0
	}
	rate := float64(present) / float64(total) * 100
	// Round to 2 decimal places
	return float64(int(rate*100)) / 100
}
