package services

import (
	"context"
	"fmt"
	"time"

	"github.com/breakpilot/consent-service/internal/database"
	"github.com/breakpilot/consent-service/internal/models"
	"github.com/breakpilot/consent-service/internal/services/matrix"

	"github.com/google/uuid"
)

// SchoolService handles school management operations
type SchoolService struct {
	db     *database.DB
	matrix *matrix.MatrixService
}

// NewSchoolService creates a new school service
func NewSchoolService(db *database.DB, matrixService *matrix.MatrixService) *SchoolService {
	return &SchoolService{
		db:     db,
		matrix: matrixService,
	}
}

// ========================================
// School CRUD
// ========================================

// CreateSchool creates a new school
func (s *SchoolService) CreateSchool(ctx context.Context, req models.CreateSchoolRequest) (*models.School, error) {
	school := &models.School{
		ID:         uuid.New(),
		Name:       req.Name,
		ShortName:  req.ShortName,
		Type:       req.Type,
		Address:    req.Address,
		City:       req.City,
		PostalCode: req.PostalCode,
		State:      req.State,
		Country:    "DE",
		Phone:      req.Phone,
		Email:      req.Email,
		Website:    req.Website,
		IsActive:   true,
		CreatedAt:  time.Now(),
		UpdatedAt:  time.Now(),
	}

	query := `
		INSERT INTO schools (id, name, short_name, type, address, city, postal_code, state, country, phone, email, website, is_active, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
		RETURNING id`

	err := s.db.Pool.QueryRow(ctx, query,
		school.ID, school.Name, school.ShortName, school.Type, school.Address,
		school.City, school.PostalCode, school.State, school.Country, school.Phone,
		school.Email, school.Website, school.IsActive, school.CreatedAt, school.UpdatedAt,
	).Scan(&school.ID)

	if err != nil {
		return nil, fmt.Errorf("failed to create school: %w", err)
	}

	// Create default timetable slots for the school
	if err := s.createDefaultTimetableSlots(ctx, school.ID); err != nil {
		// Log but don't fail
		fmt.Printf("Warning: failed to create default timetable slots: %v\n", err)
	}

	// Create default grade scale
	if err := s.createDefaultGradeScale(ctx, school.ID); err != nil {
		fmt.Printf("Warning: failed to create default grade scale: %v\n", err)
	}

	return school, nil
}

// GetSchool retrieves a school by ID
func (s *SchoolService) GetSchool(ctx context.Context, schoolID uuid.UUID) (*models.School, error) {
	query := `
		SELECT id, name, short_name, type, address, city, postal_code, state, country, phone, email, website, matrix_server_name, logo_url, is_active, created_at, updated_at
		FROM schools
		WHERE id = $1`

	school := &models.School{}
	err := s.db.Pool.QueryRow(ctx, query, schoolID).Scan(
		&school.ID, &school.Name, &school.ShortName, &school.Type, &school.Address,
		&school.City, &school.PostalCode, &school.State, &school.Country, &school.Phone,
		&school.Email, &school.Website, &school.MatrixServerName, &school.LogoURL,
		&school.IsActive, &school.CreatedAt, &school.UpdatedAt,
	)

	if err != nil {
		return nil, fmt.Errorf("failed to get school: %w", err)
	}

	return school, nil
}

// ListSchools lists all active schools
func (s *SchoolService) ListSchools(ctx context.Context) ([]models.School, error) {
	query := `
		SELECT id, name, short_name, type, address, city, postal_code, state, country, phone, email, website, matrix_server_name, logo_url, is_active, created_at, updated_at
		FROM schools
		WHERE is_active = true
		ORDER BY name`

	rows, err := s.db.Pool.Query(ctx, query)
	if err != nil {
		return nil, fmt.Errorf("failed to list schools: %w", err)
	}
	defer rows.Close()

	var schools []models.School
	for rows.Next() {
		var school models.School
		err := rows.Scan(
			&school.ID, &school.Name, &school.ShortName, &school.Type, &school.Address,
			&school.City, &school.PostalCode, &school.State, &school.Country, &school.Phone,
			&school.Email, &school.Website, &school.MatrixServerName, &school.LogoURL,
			&school.IsActive, &school.CreatedAt, &school.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan school: %w", err)
		}
		schools = append(schools, school)
	}

	return schools, nil
}

// ========================================
// School Year Management
// ========================================

// CreateSchoolYear creates a new school year
func (s *SchoolService) CreateSchoolYear(ctx context.Context, schoolID uuid.UUID, name string, startDate, endDate time.Time) (*models.SchoolYear, error) {
	schoolYear := &models.SchoolYear{
		ID:        uuid.New(),
		SchoolID:  schoolID,
		Name:      name,
		StartDate: startDate,
		EndDate:   endDate,
		IsCurrent: false,
		CreatedAt: time.Now(),
	}

	query := `
		INSERT INTO school_years (id, school_id, name, start_date, end_date, is_current, created_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7)
		RETURNING id`

	err := s.db.Pool.QueryRow(ctx, query,
		schoolYear.ID, schoolYear.SchoolID, schoolYear.Name,
		schoolYear.StartDate, schoolYear.EndDate, schoolYear.IsCurrent, schoolYear.CreatedAt,
	).Scan(&schoolYear.ID)

	if err != nil {
		return nil, fmt.Errorf("failed to create school year: %w", err)
	}

	return schoolYear, nil
}

// SetCurrentSchoolYear sets a school year as the current one
func (s *SchoolService) SetCurrentSchoolYear(ctx context.Context, schoolID, schoolYearID uuid.UUID) error {
	// First, unset all current school years for this school
	_, err := s.db.Pool.Exec(ctx, `UPDATE school_years SET is_current = false WHERE school_id = $1`, schoolID)
	if err != nil {
		return fmt.Errorf("failed to unset current school years: %w", err)
	}

	// Then set the specified school year as current
	_, err = s.db.Pool.Exec(ctx, `UPDATE school_years SET is_current = true WHERE id = $1 AND school_id = $2`, schoolYearID, schoolID)
	if err != nil {
		return fmt.Errorf("failed to set current school year: %w", err)
	}

	return nil
}

// GetCurrentSchoolYear gets the current school year for a school
func (s *SchoolService) GetCurrentSchoolYear(ctx context.Context, schoolID uuid.UUID) (*models.SchoolYear, error) {
	query := `
		SELECT id, school_id, name, start_date, end_date, is_current, created_at
		FROM school_years
		WHERE school_id = $1 AND is_current = true`

	schoolYear := &models.SchoolYear{}
	err := s.db.Pool.QueryRow(ctx, query, schoolID).Scan(
		&schoolYear.ID, &schoolYear.SchoolID, &schoolYear.Name,
		&schoolYear.StartDate, &schoolYear.EndDate, &schoolYear.IsCurrent, &schoolYear.CreatedAt,
	)

	if err != nil {
		return nil, fmt.Errorf("failed to get current school year: %w", err)
	}

	return schoolYear, nil
}

// ========================================
// Class Management
// ========================================

// CreateClass creates a new class
func (s *SchoolService) CreateClass(ctx context.Context, schoolID uuid.UUID, req models.CreateClassRequest) (*models.Class, error) {
	schoolYearID, err := uuid.Parse(req.SchoolYearID)
	if err != nil {
		return nil, fmt.Errorf("invalid school year ID: %w", err)
	}

	class := &models.Class{
		ID:           uuid.New(),
		SchoolID:     schoolID,
		SchoolYearID: schoolYearID,
		Name:         req.Name,
		Grade:        req.Grade,
		Section:      req.Section,
		Room:         req.Room,
		IsActive:     true,
		CreatedAt:    time.Now(),
		UpdatedAt:    time.Now(),
	}

	query := `
		INSERT INTO classes (id, school_id, school_year_id, name, grade, section, room, is_active, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
		RETURNING id`

	err = s.db.Pool.QueryRow(ctx, query,
		class.ID, class.SchoolID, class.SchoolYearID, class.Name,
		class.Grade, class.Section, class.Room, class.IsActive, class.CreatedAt, class.UpdatedAt,
	).Scan(&class.ID)

	if err != nil {
		return nil, fmt.Errorf("failed to create class: %w", err)
	}

	return class, nil
}

// GetClass retrieves a class by ID
func (s *SchoolService) GetClass(ctx context.Context, classID uuid.UUID) (*models.Class, error) {
	query := `
		SELECT id, school_id, school_year_id, name, grade, section, room, matrix_info_room, matrix_rep_room, is_active, created_at, updated_at
		FROM classes
		WHERE id = $1`

	class := &models.Class{}
	err := s.db.Pool.QueryRow(ctx, query, classID).Scan(
		&class.ID, &class.SchoolID, &class.SchoolYearID, &class.Name,
		&class.Grade, &class.Section, &class.Room, &class.MatrixInfoRoom,
		&class.MatrixRepRoom, &class.IsActive, &class.CreatedAt, &class.UpdatedAt,
	)

	if err != nil {
		return nil, fmt.Errorf("failed to get class: %w", err)
	}

	return class, nil
}

// ListClasses lists all classes for a school in a school year
func (s *SchoolService) ListClasses(ctx context.Context, schoolID, schoolYearID uuid.UUID) ([]models.Class, error) {
	query := `
		SELECT id, school_id, school_year_id, name, grade, section, room, matrix_info_room, matrix_rep_room, is_active, created_at, updated_at
		FROM classes
		WHERE school_id = $1 AND school_year_id = $2 AND is_active = true
		ORDER BY grade, name`

	rows, err := s.db.Pool.Query(ctx, query, schoolID, schoolYearID)
	if err != nil {
		return nil, fmt.Errorf("failed to list classes: %w", err)
	}
	defer rows.Close()

	var classes []models.Class
	for rows.Next() {
		var class models.Class
		err := rows.Scan(
			&class.ID, &class.SchoolID, &class.SchoolYearID, &class.Name,
			&class.Grade, &class.Section, &class.Room, &class.MatrixInfoRoom,
			&class.MatrixRepRoom, &class.IsActive, &class.CreatedAt, &class.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan class: %w", err)
		}
		classes = append(classes, class)
	}

	return classes, nil
}

// ========================================
// Helper Functions
// ========================================

func (s *SchoolService) createDefaultTimetableSlots(ctx context.Context, schoolID uuid.UUID) error {
	slots := []struct {
		Number    int
		StartTime string
		EndTime   string
		IsBreak   bool
		Name      string
	}{
		{1, "08:00", "08:45", false, "1. Stunde"},
		{2, "08:45", "09:30", false, "2. Stunde"},
		{3, "09:30", "09:50", true, "Erste Pause"},
		{4, "09:50", "10:35", false, "3. Stunde"},
		{5, "10:35", "11:20", false, "4. Stunde"},
		{6, "11:20", "11:40", true, "Zweite Pause"},
		{7, "11:40", "12:25", false, "5. Stunde"},
		{8, "12:25", "13:10", false, "6. Stunde"},
		{9, "13:10", "14:00", true, "Mittagspause"},
		{10, "14:00", "14:45", false, "7. Stunde"},
		{11, "14:45", "15:30", false, "8. Stunde"},
	}

	query := `
		INSERT INTO timetable_slots (id, school_id, slot_number, start_time, end_time, is_break, name)
		VALUES ($1, $2, $3, $4, $5, $6, $7)
		ON CONFLICT (school_id, slot_number) DO NOTHING`

	for _, slot := range slots {
		_, err := s.db.Pool.Exec(ctx, query,
			uuid.New(), schoolID, slot.Number, slot.StartTime, slot.EndTime, slot.IsBreak, slot.Name,
		)
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *SchoolService) createDefaultGradeScale(ctx context.Context, schoolID uuid.UUID) error {
	query := `
		INSERT INTO grade_scales (id, school_id, name, min_value, max_value, passing_value, is_ascending, is_default, created_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
		ON CONFLICT DO NOTHING`

	_, err := s.db.Pool.Exec(ctx, query,
		uuid.New(), schoolID, "1-6 (Noten)", 1.0, 6.0, 4.0, false, true, time.Now(),
	)

	return err
}
