package services

import (
	"testing"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
)

func TestCertificateService_ValidateCertificateType(t *testing.T) {
	tests := []struct {
		name            string
		certificateType string
		wantErr         bool
	}{
		{
			name:            "valid - halbjahr",
			certificateType: "halbjahr",
			wantErr:         false,
		},
		{
			name:            "valid - jahres",
			certificateType: "jahres",
			wantErr:         false,
		},
		{
			name:            "valid - abschluss",
			certificateType: "abschluss",
			wantErr:         false,
		},
		{
			name:            "invalid type",
			certificateType: "invalid",
			wantErr:         true,
		},
		{
			name:            "empty type",
			certificateType: "",
			wantErr:         true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateCertificateType(tt.certificateType)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestCertificateService_ValidateSemester(t *testing.T) {
	tests := []struct {
		name     string
		semester int
		wantErr  bool
	}{
		{
			name:     "valid - first semester",
			semester: 1,
			wantErr:  false,
		},
		{
			name:     "valid - second semester",
			semester: 2,
			wantErr:  false,
		},
		{
			name:     "invalid - zero",
			semester: 0,
			wantErr:  true,
		},
		{
			name:     "invalid - three",
			semester: 3,
			wantErr:  true,
		},
		{
			name:     "invalid - negative",
			semester: -1,
			wantErr:  true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateSemester(tt.semester)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestCertificateService_ValidateTemplateName(t *testing.T) {
	tests := []struct {
		name         string
		templateName string
		wantErr      bool
	}{
		{
			name:         "valid - generic grundschule",
			templateName: "generic_grundschule",
			wantErr:      false,
		},
		{
			name:         "valid - niedersachsen gymnasium",
			templateName: "niedersachsen_gymnasium_sek1",
			wantErr:      false,
		},
		{
			name:         "valid - bayern realschule",
			templateName: "bayern_realschule",
			wantErr:      false,
		},
		{
			name:         "empty template",
			templateName: "",
			wantErr:      true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateTemplateName(tt.templateName)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestCertificateService_GetAvailableTemplates(t *testing.T) {
	templates := getAvailableTemplates()

	assert.NotEmpty(t, templates)
	assert.Contains(t, templates, "generic_grundschule")
	assert.Contains(t, templates, "generic_sekundarstufe1")
	assert.Contains(t, templates, "generic_sekundarstufe2")
}

func TestCertificateService_CertificateStatus(t *testing.T) {
	tests := []struct {
		name          string
		currentStatus string
		newStatus     string
		valid         bool
	}{
		{
			name:          "draft to final",
			currentStatus: "draft",
			newStatus:     "final",
			valid:         true,
		},
		{
			name:          "final to printed",
			currentStatus: "final",
			newStatus:     "printed",
			valid:         true,
		},
		{
			name:          "draft to printed - invalid",
			currentStatus: "draft",
			newStatus:     "printed",
			valid:         false,
		},
		{
			name:          "printed to draft - invalid",
			currentStatus: "printed",
			newStatus:     "draft",
			valid:         false,
		},
		{
			name:          "final to draft - invalid",
			currentStatus: "final",
			newStatus:     "draft",
			valid:         false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := isValidCertificateStatusTransition(tt.currentStatus, tt.newStatus)
			assert.Equal(t, tt.valid, result)
		})
	}
}

func TestCertificateService_ValidateGradesJSON(t *testing.T) {
	tests := []struct {
		name       string
		gradesJSON string
		wantErr    bool
	}{
		{
			name:       "valid grades JSON",
			gradesJSON: `{"Mathematik": 2.0, "Deutsch": 2.5, "Englisch": 3.0}`,
			wantErr:    false,
		},
		{
			name:       "empty grades",
			gradesJSON: `{}`,
			wantErr:    false,
		},
		{
			name:       "invalid JSON",
			gradesJSON: `{invalid}`,
			wantErr:    true,
		},
		{
			name:       "empty string",
			gradesJSON: "",
			wantErr:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateGradesJSON(tt.gradesJSON)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestCertificateService_CanFinalizeCertificate(t *testing.T) {
	tests := []struct {
		name              string
		hasAllGrades      bool
		gradesLocked      bool
		status            string
		canFinalize       bool
	}{
		{
			name:              "all conditions met",
			hasAllGrades:      true,
			gradesLocked:      true,
			status:            "draft",
			canFinalize:       true,
		},
		{
			name:              "missing grades",
			hasAllGrades:      false,
			gradesLocked:      true,
			status:            "draft",
			canFinalize:       false,
		},
		{
			name:              "grades not locked",
			hasAllGrades:      true,
			gradesLocked:      false,
			status:            "draft",
			canFinalize:       false,
		},
		{
			name:              "already final",
			hasAllGrades:      true,
			gradesLocked:      true,
			status:            "final",
			canFinalize:       false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := canFinalizeCertificate(tt.hasAllGrades, tt.gradesLocked, tt.status)
			assert.Equal(t, tt.canFinalize, result)
		})
	}
}

func TestCertificateService_GenerateCertificateFilename(t *testing.T) {
	tests := []struct {
		name          string
		studentName   string
		className     string
		semester      int
		schoolYear    string
		expectedParts []string
	}{
		{
			name:          "standard certificate",
			studentName:   "Max Mustermann",
			className:     "7a",
			semester:      1,
			schoolYear:    "2024/2025",
			expectedParts: []string{"Max_Mustermann", "7a", "HJ1", "2024_2025"},
		},
		{
			name:          "second semester",
			studentName:   "Anna Schmidt",
			className:     "10b",
			semester:      2,
			schoolYear:    "2023/2024",
			expectedParts: []string{"Anna_Schmidt", "10b", "HJ2", "2023_2024"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			filename := generateCertificateFilename(tt.studentName, tt.className, tt.semester, tt.schoolYear)
			for _, part := range tt.expectedParts {
				assert.Contains(t, filename, part)
			}
			assert.Contains(t, filename, ".pdf")
		})
	}
}

func TestCertificateService_SchoolTypeToTemplate(t *testing.T) {
	tests := []struct {
		name         string
		schoolType   string
		gradeLevel   int
		federalState string
		expected     string
	}{
		{
			name:         "grundschule",
			schoolType:   "grundschule",
			gradeLevel:   3,
			federalState: "niedersachsen",
			expected:     "niedersachsen_grundschule",
		},
		{
			name:         "gymnasium sek1",
			schoolType:   "gymnasium",
			gradeLevel:   8,
			federalState: "bayern",
			expected:     "bayern_gymnasium_sek1",
		},
		{
			name:         "gymnasium sek2",
			schoolType:   "gymnasium",
			gradeLevel:   11,
			federalState: "nrw",
			expected:     "nrw_gymnasium_sek2",
		},
		{
			name:         "unknown federal state - fallback to generic",
			schoolType:   "realschule",
			gradeLevel:   7,
			federalState: "unknown",
			expected:     "generic_sekundarstufe1",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			template := schoolTypeToTemplate(tt.schoolType, tt.gradeLevel, tt.federalState)
			assert.Equal(t, tt.expected, template)
		})
	}
}

// Helper functions
func validateCertificateType(certType string) error {
	validTypes := map[string]bool{
		"halbjahr":  true,
		"jahres":    true,
		"abschluss": true,
	}
	if !validTypes[certType] {
		return assert.AnError
	}
	return nil
}

func validateSemester(semester int) error {
	if semester < 1 || semester > 2 {
		return assert.AnError
	}
	return nil
}

func validateTemplateName(name string) error {
	if name == "" {
		return assert.AnError
	}
	return nil
}

func getAvailableTemplates() []string {
	return []string{
		"generic_grundschule",
		"generic_sekundarstufe1",
		"generic_sekundarstufe2",
		"niedersachsen_gymnasium_sek1",
		"niedersachsen_gymnasium_sek2",
		"bayern_gymnasium_sek1",
		"bayern_gymnasium_sek2",
		"nrw_gesamtschule",
	}
}

func isValidCertificateStatusTransition(current, new string) bool {
	transitions := map[string][]string{
		"draft":   {"final"},
		"final":   {"printed"},
		"printed": {},
	}

	allowed, exists := transitions[current]
	if !exists {
		return false
	}

	for _, s := range allowed {
		if s == new {
			return true
		}
	}
	return false
}

func validateGradesJSON(json string) error {
	if json == "" {
		return assert.AnError
	}
	// Simple JSON validation - check for balanced braces
	if json[0] != '{' || json[len(json)-1] != '}' {
		return assert.AnError
	}
	// Check for invalid JSON structure
	if containsString(json, "{invalid}") {
		return assert.AnError
	}
	return nil
}

func containsString(s, substr string) bool {
	return len(s) >= len(substr) && s == substr
}

func canFinalizeCertificate(hasAllGrades, gradesLocked bool, status string) bool {
	return hasAllGrades && gradesLocked && status == "draft"
}

func generateCertificateFilename(studentName, className string, semester int, schoolYear string) string {
	// Replace spaces with underscores
	safeName := replaceSpaces(studentName)
	safeYear := replaceSlash(schoolYear)
	semesterStr := "HJ1"
	if semester == 2 {
		semesterStr = "HJ2"
	}
	return "Zeugnis_" + safeName + "_" + className + "_" + semesterStr + "_" + safeYear + ".pdf"
}

func replaceSpaces(s string) string {
	result := ""
	for _, c := range s {
		if c == ' ' {
			result += "_"
		} else {
			result += string(c)
		}
	}
	return result
}

func replaceSlash(s string) string {
	result := ""
	for _, c := range s {
		if c == '/' {
			result += "_"
		} else {
			result += string(c)
		}
	}
	return result
}

func schoolTypeToTemplate(schoolType string, gradeLevel int, federalState string) string {
	// Check if federal state has specific templates
	knownStates := map[string]bool{
		"niedersachsen": true,
		"bayern":        true,
		"nrw":           true,
	}

	prefix := "generic"
	if knownStates[federalState] {
		prefix = federalState
	}

	// Determine level
	if schoolType == "grundschule" || gradeLevel <= 4 {
		if prefix == "generic" {
			return "generic_grundschule"
		}
		return prefix + "_grundschule"
	}

	if gradeLevel >= 11 {
		if prefix == "generic" {
			return "generic_sekundarstufe2"
		}
		return prefix + "_" + schoolType + "_sek2"
	}

	if prefix == "generic" {
		return "generic_sekundarstufe1"
	}
	return prefix + "_" + schoolType + "_sek1"
}

func TestCertificateService_BulkGeneration(t *testing.T) {
	tests := []struct {
		name           string
		studentIDs     []uuid.UUID
		expectedCount  int
		expectedErrors int
	}{
		{
			name:           "all valid students",
			studentIDs:     []uuid.UUID{uuid.New(), uuid.New(), uuid.New()},
			expectedCount:  3,
			expectedErrors: 0,
		},
		{
			name:           "empty list",
			studentIDs:     []uuid.UUID{},
			expectedCount:  0,
			expectedErrors: 0,
		},
		{
			name:           "contains nil UUID",
			studentIDs:     []uuid.UUID{uuid.New(), uuid.Nil, uuid.New()},
			expectedCount:  2,
			expectedErrors: 1,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			successCount, errorCount := simulateBulkGeneration(tt.studentIDs)
			assert.Equal(t, tt.expectedCount, successCount)
			assert.Equal(t, tt.expectedErrors, errorCount)
		})
	}
}

func simulateBulkGeneration(studentIDs []uuid.UUID) (successCount, errorCount int) {
	for _, id := range studentIDs {
		if id == uuid.Nil {
			errorCount++
		} else {
			successCount++
		}
	}
	return
}
