package academy

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
)

// ============================================================================
// Enrollment Operations
// ============================================================================

// CreateEnrollment creates a new enrollment
func (s *Store) CreateEnrollment(ctx context.Context, enrollment *Enrollment) error {
	enrollment.ID = uuid.New()
	enrollment.CreatedAt = time.Now().UTC()
	enrollment.UpdatedAt = enrollment.CreatedAt
	if enrollment.Status == "" {
		enrollment.Status = EnrollmentStatusNotStarted
	}

	_, err := s.pool.Exec(ctx, `
		INSERT INTO academy_enrollments (
			id, tenant_id, course_id, user_id, user_name, user_email,
			status, progress_percent, current_lesson_index,
			started_at, completed_at, deadline,
			created_at, updated_at
		) VALUES (
			$1, $2, $3, $4, $5, $6,
			$7, $8, $9,
			$10, $11, $12,
			$13, $14
		)
	`,
		enrollment.ID, enrollment.TenantID, enrollment.CourseID, enrollment.UserID, enrollment.UserName, enrollment.UserEmail,
		string(enrollment.Status), enrollment.ProgressPercent, enrollment.CurrentLessonIndex,
		enrollment.StartedAt, enrollment.CompletedAt, enrollment.Deadline,
		enrollment.CreatedAt, enrollment.UpdatedAt,
	)

	return err
}

// GetEnrollment retrieves an enrollment by ID
func (s *Store) GetEnrollment(ctx context.Context, id uuid.UUID) (*Enrollment, error) {
	var enrollment Enrollment
	var status string

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, tenant_id, course_id, user_id, user_name, user_email,
			status, progress_percent, current_lesson_index,
			started_at, completed_at, deadline,
			created_at, updated_at
		FROM academy_enrollments WHERE id = $1
	`, id).Scan(
		&enrollment.ID, &enrollment.TenantID, &enrollment.CourseID, &enrollment.UserID, &enrollment.UserName, &enrollment.UserEmail,
		&status, &enrollment.ProgressPercent, &enrollment.CurrentLessonIndex,
		&enrollment.StartedAt, &enrollment.CompletedAt, &enrollment.Deadline,
		&enrollment.CreatedAt, &enrollment.UpdatedAt,
	)

	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	enrollment.Status = EnrollmentStatus(status)
	return &enrollment, nil
}

// ListEnrollments lists enrollments for a tenant with optional filters
func (s *Store) ListEnrollments(ctx context.Context, tenantID uuid.UUID, filters *EnrollmentFilters) ([]Enrollment, int, error) {
	// Count query
	countQuery := "SELECT COUNT(*) FROM academy_enrollments WHERE tenant_id = $1"
	countArgs := []interface{}{tenantID}
	countArgIdx := 2

	// List query
	query := `
		SELECT
			id, tenant_id, course_id, user_id, user_name, user_email,
			status, progress_percent, current_lesson_index,
			started_at, completed_at, deadline,
			created_at, updated_at
		FROM academy_enrollments WHERE tenant_id = $1`

	args := []interface{}{tenantID}
	argIdx := 2

	if filters != nil {
		if filters.CourseID != nil {
			query += fmt.Sprintf(" AND course_id = $%d", argIdx)
			args = append(args, *filters.CourseID)
			argIdx++

			countQuery += fmt.Sprintf(" AND course_id = $%d", countArgIdx)
			countArgs = append(countArgs, *filters.CourseID)
			countArgIdx++
		}
		if filters.UserID != nil {
			query += fmt.Sprintf(" AND user_id = $%d", argIdx)
			args = append(args, *filters.UserID)
			argIdx++

			countQuery += fmt.Sprintf(" AND user_id = $%d", countArgIdx)
			countArgs = append(countArgs, *filters.UserID)
			countArgIdx++
		}
		if filters.Status != "" {
			query += fmt.Sprintf(" AND status = $%d", argIdx)
			args = append(args, string(filters.Status))
			argIdx++

			countQuery += fmt.Sprintf(" AND status = $%d", countArgIdx)
			countArgs = append(countArgs, string(filters.Status))
			countArgIdx++
		}
	}

	// Get total count
	var total int
	err := s.pool.QueryRow(ctx, countQuery, countArgs...).Scan(&total)
	if err != nil {
		return nil, 0, err
	}

	query += " ORDER BY created_at DESC"

	if filters != nil && filters.Limit > 0 {
		query += fmt.Sprintf(" LIMIT $%d", argIdx)
		args = append(args, filters.Limit)
		argIdx++

		if filters.Offset > 0 {
			query += fmt.Sprintf(" OFFSET $%d", argIdx)
			args = append(args, filters.Offset)
			argIdx++
		}
	}

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

	var enrollments []Enrollment
	for rows.Next() {
		var enrollment Enrollment
		var status string

		err := rows.Scan(
			&enrollment.ID, &enrollment.TenantID, &enrollment.CourseID, &enrollment.UserID, &enrollment.UserName, &enrollment.UserEmail,
			&status, &enrollment.ProgressPercent, &enrollment.CurrentLessonIndex,
			&enrollment.StartedAt, &enrollment.CompletedAt, &enrollment.Deadline,
			&enrollment.CreatedAt, &enrollment.UpdatedAt,
		)
		if err != nil {
			return nil, 0, err
		}

		enrollment.Status = EnrollmentStatus(status)
		enrollments = append(enrollments, enrollment)
	}

	if enrollments == nil {
		enrollments = []Enrollment{}
	}

	return enrollments, total, nil
}

// UpdateEnrollmentProgress updates the progress for an enrollment
func (s *Store) UpdateEnrollmentProgress(ctx context.Context, id uuid.UUID, progress int, currentLesson int) error {
	now := time.Now().UTC()

	_, err := s.pool.Exec(ctx, `
		UPDATE academy_enrollments SET
			progress_percent = $2,
			current_lesson_index = $3,
			status = CASE
				WHEN $2 >= 100 THEN 'completed'
				WHEN $2 > 0 THEN 'in_progress'
				ELSE status
			END,
			started_at = CASE
				WHEN started_at IS NULL AND $2 > 0 THEN $4
				ELSE started_at
			END,
			completed_at = CASE
				WHEN $2 >= 100 THEN $4
				ELSE completed_at
			END,
			updated_at = $4
		WHERE id = $1
	`, id, progress, currentLesson, now)

	return err
}

// CompleteEnrollment marks an enrollment as completed
func (s *Store) CompleteEnrollment(ctx context.Context, id uuid.UUID) error {
	now := time.Now().UTC()

	_, err := s.pool.Exec(ctx, `
		UPDATE academy_enrollments SET
			status = 'completed',
			progress_percent = 100,
			completed_at = $2,
			updated_at = $2
		WHERE id = $1
	`, id, now)

	return err
}

// ============================================================================
// Certificate Operations
// ============================================================================

// GetCertificate retrieves a certificate by ID
func (s *Store) GetCertificate(ctx context.Context, id uuid.UUID) (*Certificate, error) {
	var cert Certificate

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, enrollment_id, user_name, course_title,
			issued_at, valid_until, pdf_url
		FROM academy_certificates WHERE id = $1
	`, id).Scan(
		&cert.ID, &cert.EnrollmentID, &cert.UserName, &cert.CourseTitle,
		&cert.IssuedAt, &cert.ValidUntil, &cert.PDFURL,
	)

	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	return &cert, nil
}

// GetCertificateByEnrollment retrieves a certificate by enrollment ID
func (s *Store) GetCertificateByEnrollment(ctx context.Context, enrollmentID uuid.UUID) (*Certificate, error) {
	var cert Certificate

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, enrollment_id, user_name, course_title,
			issued_at, valid_until, pdf_url
		FROM academy_certificates WHERE enrollment_id = $1
	`, enrollmentID).Scan(
		&cert.ID, &cert.EnrollmentID, &cert.UserName, &cert.CourseTitle,
		&cert.IssuedAt, &cert.ValidUntil, &cert.PDFURL,
	)

	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	return &cert, nil
}

// CreateCertificate creates a new certificate
func (s *Store) CreateCertificate(ctx context.Context, cert *Certificate) error {
	cert.ID = uuid.New()
	cert.IssuedAt = time.Now().UTC()

	_, err := s.pool.Exec(ctx, `
		INSERT INTO academy_certificates (
			id, enrollment_id, user_name, course_title,
			issued_at, valid_until, pdf_url
		) VALUES (
			$1, $2, $3, $4,
			$5, $6, $7
		)
	`,
		cert.ID, cert.EnrollmentID, cert.UserName, cert.CourseTitle,
		cert.IssuedAt, cert.ValidUntil, cert.PDFURL,
	)

	return err
}

// ============================================================================
// Statistics
// ============================================================================

// GetStatistics returns aggregated academy statistics for a tenant
func (s *Store) GetStatistics(ctx context.Context, tenantID uuid.UUID) (*AcademyStatistics, error) {
	stats := &AcademyStatistics{}

	// Total active courses
	s.pool.QueryRow(ctx,
		"SELECT COUNT(*) FROM academy_courses WHERE tenant_id = $1 AND is_active = true",
		tenantID).Scan(&stats.TotalCourses)

	// Total enrollments
	s.pool.QueryRow(ctx,
		"SELECT COUNT(*) FROM academy_enrollments WHERE tenant_id = $1",
		tenantID).Scan(&stats.TotalEnrollments)

	// Completion rate
	if stats.TotalEnrollments > 0 {
		var completed int
		s.pool.QueryRow(ctx,
			"SELECT COUNT(*) FROM academy_enrollments WHERE tenant_id = $1 AND status = 'completed'",
			tenantID).Scan(&completed)
		stats.CompletionRate = float64(completed) / float64(stats.TotalEnrollments) * 100
	}

	// Overdue count (past deadline, not completed)
	s.pool.QueryRow(ctx,
		`SELECT COUNT(*) FROM academy_enrollments
		 WHERE tenant_id = $1
		 AND status NOT IN ('completed', 'expired')
		 AND deadline IS NOT NULL
		 AND deadline < NOW()`,
		tenantID).Scan(&stats.OverdueCount)

	// Average completion days
	s.pool.QueryRow(ctx,
		`SELECT COALESCE(AVG(EXTRACT(EPOCH FROM (completed_at - started_at)) / 86400), 0)
		 FROM academy_enrollments
		 WHERE tenant_id = $1
		 AND status = 'completed'
		 AND started_at IS NOT NULL
		 AND completed_at IS NOT NULL`,
		tenantID).Scan(&stats.AvgCompletionDays)

	return stats, nil
}
