package academy

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

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

// Store handles academy data persistence
type Store struct {
	pool *pgxpool.Pool
}

// NewStore creates a new academy store
func NewStore(pool *pgxpool.Pool) *Store {
	return &Store{pool: pool}
}

// ============================================================================
// Course CRUD Operations
// ============================================================================

// CreateCourse creates a new course
func (s *Store) CreateCourse(ctx context.Context, course *Course) error {
	course.ID = uuid.New()
	course.CreatedAt = time.Now().UTC()
	course.UpdatedAt = course.CreatedAt
	if !course.IsActive {
		course.IsActive = true
	}

	requiredForRoles, _ := json.Marshal(course.RequiredForRoles)

	_, err := s.pool.Exec(ctx, `
		INSERT INTO academy_courses (
			id, tenant_id, title, description, category,
			duration_minutes, required_for_roles, is_active,
			created_at, updated_at
		) VALUES (
			$1, $2, $3, $4, $5,
			$6, $7, $8,
			$9, $10
		)
	`,
		course.ID, course.TenantID, course.Title, course.Description, string(course.Category),
		course.DurationMinutes, requiredForRoles, course.IsActive,
		course.CreatedAt, course.UpdatedAt,
	)

	return err
}

// GetCourse retrieves a course by ID
func (s *Store) GetCourse(ctx context.Context, id uuid.UUID) (*Course, error) {
	var course Course
	var category string
	var requiredForRoles []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, tenant_id, title, description, category,
			duration_minutes, required_for_roles, is_active,
			created_at, updated_at
		FROM academy_courses WHERE id = $1
	`, id).Scan(
		&course.ID, &course.TenantID, &course.Title, &course.Description, &category,
		&course.DurationMinutes, &requiredForRoles, &course.IsActive,
		&course.CreatedAt, &course.UpdatedAt,
	)

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

	course.Category = CourseCategory(category)
	json.Unmarshal(requiredForRoles, &course.RequiredForRoles)
	if course.RequiredForRoles == nil {
		course.RequiredForRoles = []string{}
	}

	// Load lessons for this course
	lessons, err := s.ListLessons(ctx, course.ID)
	if err != nil {
		return nil, err
	}
	course.Lessons = lessons

	return &course, nil
}

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

	// List query
	query := `
		SELECT
			id, tenant_id, title, description, category,
			duration_minutes, required_for_roles, is_active,
			created_at, updated_at
		FROM academy_courses WHERE tenant_id = $1`

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

	if filters != nil {
		if filters.Category != "" {
			query += fmt.Sprintf(" AND category = $%d", argIdx)
			args = append(args, string(filters.Category))
			argIdx++

			countQuery += fmt.Sprintf(" AND category = $%d", countArgIdx)
			countArgs = append(countArgs, string(filters.Category))
			countArgIdx++
		}
		if filters.IsActive != nil {
			query += fmt.Sprintf(" AND is_active = $%d", argIdx)
			args = append(args, *filters.IsActive)
			argIdx++

			countQuery += fmt.Sprintf(" AND is_active = $%d", countArgIdx)
			countArgs = append(countArgs, *filters.IsActive)
			countArgIdx++
		}
		if filters.Search != "" {
			query += fmt.Sprintf(" AND (title ILIKE $%d OR description ILIKE $%d)", argIdx, argIdx)
			args = append(args, "%"+filters.Search+"%")
			argIdx++

			countQuery += fmt.Sprintf(" AND (title ILIKE $%d OR description ILIKE $%d)", countArgIdx, countArgIdx)
			countArgs = append(countArgs, "%"+filters.Search+"%")
			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 courses []Course
	for rows.Next() {
		var course Course
		var category string
		var requiredForRoles []byte

		err := rows.Scan(
			&course.ID, &course.TenantID, &course.Title, &course.Description, &category,
			&course.DurationMinutes, &requiredForRoles, &course.IsActive,
			&course.CreatedAt, &course.UpdatedAt,
		)
		if err != nil {
			return nil, 0, err
		}

		course.Category = CourseCategory(category)
		json.Unmarshal(requiredForRoles, &course.RequiredForRoles)
		if course.RequiredForRoles == nil {
			course.RequiredForRoles = []string{}
		}

		courses = append(courses, course)
	}

	if courses == nil {
		courses = []Course{}
	}

	return courses, total, nil
}

// UpdateCourse updates a course
func (s *Store) UpdateCourse(ctx context.Context, course *Course) error {
	course.UpdatedAt = time.Now().UTC()

	requiredForRoles, _ := json.Marshal(course.RequiredForRoles)

	_, err := s.pool.Exec(ctx, `
		UPDATE academy_courses SET
			title = $2, description = $3, category = $4,
			duration_minutes = $5, required_for_roles = $6, is_active = $7,
			updated_at = $8
		WHERE id = $1
	`,
		course.ID, course.Title, course.Description, string(course.Category),
		course.DurationMinutes, requiredForRoles, course.IsActive,
		course.UpdatedAt,
	)

	return err
}

// DeleteCourse deletes a course and its related data (via CASCADE)
func (s *Store) DeleteCourse(ctx context.Context, id uuid.UUID) error {
	_, err := s.pool.Exec(ctx, "DELETE FROM academy_courses WHERE id = $1", id)
	return err
}

// ============================================================================
// Lesson Operations
// ============================================================================

// CreateLesson creates a new lesson
func (s *Store) CreateLesson(ctx context.Context, lesson *Lesson) error {
	lesson.ID = uuid.New()

	quizQuestions, _ := json.Marshal(lesson.QuizQuestions)

	_, err := s.pool.Exec(ctx, `
		INSERT INTO academy_lessons (
			id, course_id, title, description, lesson_type,
			content_url, duration_minutes, order_index, quiz_questions
		) VALUES (
			$1, $2, $3, $4, $5,
			$6, $7, $8, $9
		)
	`,
		lesson.ID, lesson.CourseID, lesson.Title, lesson.Description, string(lesson.LessonType),
		lesson.ContentURL, lesson.DurationMinutes, lesson.OrderIndex, quizQuestions,
	)

	return err
}

// ListLessons lists lessons for a course ordered by order_index
func (s *Store) ListLessons(ctx context.Context, courseID uuid.UUID) ([]Lesson, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT
			id, course_id, title, description, lesson_type,
			content_url, duration_minutes, order_index, quiz_questions
		FROM academy_lessons WHERE course_id = $1
		ORDER BY order_index ASC
	`, courseID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var lessons []Lesson
	for rows.Next() {
		var lesson Lesson
		var lessonType string
		var quizQuestions []byte

		err := rows.Scan(
			&lesson.ID, &lesson.CourseID, &lesson.Title, &lesson.Description, &lessonType,
			&lesson.ContentURL, &lesson.DurationMinutes, &lesson.OrderIndex, &quizQuestions,
		)
		if err != nil {
			return nil, err
		}

		lesson.LessonType = LessonType(lessonType)
		json.Unmarshal(quizQuestions, &lesson.QuizQuestions)
		if lesson.QuizQuestions == nil {
			lesson.QuizQuestions = []QuizQuestion{}
		}

		lessons = append(lessons, lesson)
	}

	if lessons == nil {
		lessons = []Lesson{}
	}

	return lessons, nil
}

// GetLesson retrieves a single lesson by ID
func (s *Store) GetLesson(ctx context.Context, id uuid.UUID) (*Lesson, error) {
	var lesson Lesson
	var lessonType string
	var quizQuestions []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, course_id, title, description, lesson_type,
			content_url, duration_minutes, order_index, quiz_questions
		FROM academy_lessons WHERE id = $1
	`, id).Scan(
		&lesson.ID, &lesson.CourseID, &lesson.Title, &lesson.Description, &lessonType,
		&lesson.ContentURL, &lesson.DurationMinutes, &lesson.OrderIndex, &quizQuestions,
	)

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

	lesson.LessonType = LessonType(lessonType)
	json.Unmarshal(quizQuestions, &lesson.QuizQuestions)
	if lesson.QuizQuestions == nil {
		lesson.QuizQuestions = []QuizQuestion{}
	}

	return &lesson, nil
}

// UpdateLesson updates a lesson's content, title, and quiz questions
func (s *Store) UpdateLesson(ctx context.Context, lesson *Lesson) error {
	quizQuestions, _ := json.Marshal(lesson.QuizQuestions)

	_, err := s.pool.Exec(ctx, `
		UPDATE academy_lessons SET
			title = $2, description = $3, content_url = $4,
			duration_minutes = $5, quiz_questions = $6
		WHERE id = $1
	`,
		lesson.ID, lesson.Title, lesson.Description,
		lesson.ContentURL, lesson.DurationMinutes, quizQuestions,
	)

	return err
}
