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"
)

// AttendanceService handles attendance tracking and notifications
type AttendanceService struct {
	db     *database.DB
	matrix *matrix.MatrixService
}

// NewAttendanceService creates a new attendance service
func NewAttendanceService(db *database.DB, matrixService *matrix.MatrixService) *AttendanceService {
	return &AttendanceService{
		db:     db,
		matrix: matrixService,
	}
}

// ========================================
// Attendance Recording
// ========================================

// RecordAttendance records a student's attendance for a specific lesson
func (s *AttendanceService) RecordAttendance(ctx context.Context, req models.RecordAttendanceRequest, recordedByUserID uuid.UUID) (*models.AttendanceRecord, error) {
	studentID, err := uuid.Parse(req.StudentID)
	if err != nil {
		return nil, fmt.Errorf("invalid student ID: %w", err)
	}

	slotID, err := uuid.Parse(req.SlotID)
	if err != nil {
		return nil, fmt.Errorf("invalid slot ID: %w", err)
	}

	date, err := time.Parse("2006-01-02", req.Date)
	if err != nil {
		return nil, fmt.Errorf("invalid date format: %w", err)
	}

	record := &models.AttendanceRecord{
		ID:         uuid.New(),
		StudentID:  studentID,
		Date:       date,
		SlotID:     slotID,
		Status:     req.Status,
		RecordedBy: recordedByUserID,
		Note:       req.Note,
		CreatedAt:  time.Now(),
		UpdatedAt:  time.Now(),
	}

	query := `
		INSERT INTO attendance_records (id, student_id, date, slot_id, status, recorded_by, note, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
		ON CONFLICT (student_id, date, slot_id)
		DO UPDATE SET status = EXCLUDED.status, note = EXCLUDED.note, updated_at = EXCLUDED.updated_at
		RETURNING id`

	err = s.db.Pool.QueryRow(ctx, query,
		record.ID, record.StudentID, record.Date, record.SlotID,
		record.Status, record.RecordedBy, record.Note, record.CreatedAt, record.UpdatedAt,
	).Scan(&record.ID)

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

	// If student is absent, send notification to parents
	if record.Status == models.AttendanceAbsent || record.Status == models.AttendancePending {
		go s.notifyParentsOfAbsence(context.Background(), record)
	}

	return record, nil
}

// RecordBulkAttendance records attendance for multiple students at once
func (s *AttendanceService) RecordBulkAttendance(ctx context.Context, classID uuid.UUID, date string, slotID uuid.UUID, records []struct {
	StudentID string
	Status    string
	Note      *string
}, recordedByUserID uuid.UUID) error {
	parsedDate, err := time.Parse("2006-01-02", date)
	if err != nil {
		return fmt.Errorf("invalid date format: %w", err)
	}

	for _, rec := range records {
		studentID, err := uuid.Parse(rec.StudentID)
		if err != nil {
			continue
		}

		query := `
			INSERT INTO attendance_records (id, student_id, date, slot_id, status, recorded_by, note, created_at, updated_at)
			VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
			ON CONFLICT (student_id, date, slot_id)
			DO UPDATE SET status = EXCLUDED.status, note = EXCLUDED.note, updated_at = NOW()`

		_, err = s.db.Pool.Exec(ctx, query,
			uuid.New(), studentID, parsedDate, slotID, rec.Status, recordedByUserID, rec.Note,
		)
		if err != nil {
			return fmt.Errorf("failed to record attendance for student %s: %w", rec.StudentID, err)
		}

		// Notify parents if absent
		if rec.Status == models.AttendanceAbsent || rec.Status == models.AttendancePending {
			go s.notifyParentsOfAbsenceByStudentID(context.Background(), studentID, parsedDate, slotID)
		}
	}

	return nil
}

// GetAttendanceByClass gets attendance records for a class on a specific date
func (s *AttendanceService) GetAttendanceByClass(ctx context.Context, classID uuid.UUID, date string) (*models.ClassAttendanceOverview, error) {
	parsedDate, err := time.Parse("2006-01-02", date)
	if err != nil {
		return nil, fmt.Errorf("invalid date format: %w", err)
	}

	// Get class info
	classQuery := `SELECT id, school_id, school_year_id, name, grade, section, room, is_active FROM classes WHERE id = $1`
	class := &models.Class{}
	err = s.db.Pool.QueryRow(ctx, classQuery, classID).Scan(
		&class.ID, &class.SchoolID, &class.SchoolYearID, &class.Name,
		&class.Grade, &class.Section, &class.Room, &class.IsActive,
	)
	if err != nil {
		return nil, fmt.Errorf("failed to get class: %w", err)
	}

	// Get total students
	var totalStudents int
	err = s.db.Pool.QueryRow(ctx, `SELECT COUNT(*) FROM students WHERE class_id = $1 AND is_active = true`, classID).Scan(&totalStudents)
	if err != nil {
		return nil, fmt.Errorf("failed to count students: %w", err)
	}

	// Get attendance records for the date
	recordsQuery := `
		SELECT ar.id, ar.student_id, ar.date, ar.slot_id, ar.status, ar.recorded_by, ar.note, ar.created_at, ar.updated_at
		FROM attendance_records ar
		JOIN students s ON ar.student_id = s.id
		WHERE s.class_id = $1 AND ar.date = $2
		ORDER BY ar.slot_id`

	rows, err := s.db.Pool.Query(ctx, recordsQuery, classID, parsedDate)
	if err != nil {
		return nil, fmt.Errorf("failed to get attendance records: %w", err)
	}
	defer rows.Close()

	var records []models.AttendanceRecord
	presentCount := 0
	absentCount := 0
	lateCount := 0

	seenStudents := make(map[uuid.UUID]bool)

	for rows.Next() {
		var record models.AttendanceRecord
		err := rows.Scan(
			&record.ID, &record.StudentID, &record.Date, &record.SlotID,
			&record.Status, &record.RecordedBy, &record.Note, &record.CreatedAt, &record.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan attendance record: %w", err)
		}
		records = append(records, record)

		// Count unique students for summary (use first slot's status)
		if !seenStudents[record.StudentID] {
			seenStudents[record.StudentID] = true
			switch record.Status {
			case models.AttendancePresent:
				presentCount++
			case models.AttendanceAbsent, models.AttendanceAbsentExcused, models.AttendanceAbsentUnexcused, models.AttendancePending:
				absentCount++
			case models.AttendanceLate, models.AttendanceLateExcused:
				lateCount++
			}
		}
	}

	return &models.ClassAttendanceOverview{
		Class:         *class,
		Date:          parsedDate,
		TotalStudents: totalStudents,
		PresentCount:  presentCount,
		AbsentCount:   absentCount,
		LateCount:     lateCount,
		Records:       records,
	}, nil
}

// GetStudentAttendance gets attendance history for a student
func (s *AttendanceService) GetStudentAttendance(ctx context.Context, studentID uuid.UUID, startDate, endDate time.Time) ([]models.AttendanceRecord, error) {
	query := `
		SELECT id, student_id, timetable_entry_id, date, slot_id, status, recorded_by, note, created_at, updated_at
		FROM attendance_records
		WHERE student_id = $1 AND date >= $2 AND date <= $3
		ORDER BY date DESC, slot_id`

	rows, err := s.db.Pool.Query(ctx, query, studentID, startDate, endDate)
	if err != nil {
		return nil, fmt.Errorf("failed to get student attendance: %w", err)
	}
	defer rows.Close()

	var records []models.AttendanceRecord
	for rows.Next() {
		var record models.AttendanceRecord
		err := rows.Scan(
			&record.ID, &record.StudentID, &record.TimetableEntryID, &record.Date,
			&record.SlotID, &record.Status, &record.RecordedBy, &record.Note,
			&record.CreatedAt, &record.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan attendance record: %w", err)
		}
		records = append(records, record)
	}

	return records, nil
}

