-- name: GetPreviousProgram :one
-- Immediate predecessor by sort_order within the same category (gaps in sort_order are allowed).
SELECT
    p2.*
FROM
    programs AS p1
    INNER JOIN programs AS p2 ON p2.category = p1.category
        AND (
            p2.sort_order < p1.sort_order
            OR (
                p2.sort_order = p1.sort_order
                AND p2.id < p1.id
            )
        )
WHERE
    p1.id = $1
ORDER BY
    p2.sort_order DESC,
    p2.id DESC
LIMIT 1;

-- name: GetPreviousCourseInProgram :one
SELECT
    c2.*
FROM
    courses AS c1
    INNER JOIN courses AS c2 ON c2.program_id = c1.program_id
        AND (
            c2.sort_order < c1.sort_order
            OR (
                c2.sort_order = c1.sort_order
                AND c2.id < c1.id
            )
        )
WHERE
    c1.id = $1
ORDER BY
    c2.sort_order DESC,
    c2.id DESC
LIMIT 1;

-- name: GetPreviousModuleInCourse :one
SELECT
    m2.*
FROM
    modules AS m1
    INNER JOIN modules AS m2 ON m2.course_id = m1.course_id
        AND (
            m2.sort_order < m1.sort_order
            OR (
                m2.sort_order = m1.sort_order
                AND m2.id < m1.id
            )
        )
WHERE
    m1.id = $1
ORDER BY
    m2.sort_order DESC,
    m2.id DESC
LIMIT 1;

-- name: GetPreviousLessonInModule :one
SELECT
    l2.*
FROM
    lessons AS l1
    INNER JOIN lessons AS l2 ON l2.module_id = l1.module_id
        AND l2.publish_status = 'PUBLISHED'
        AND l1.publish_status = 'PUBLISHED'
        AND (
            l2.sort_order < l1.sort_order
            OR (
                l2.sort_order = l1.sort_order
                AND l2.id < l1.id
            )
        )
WHERE
    l1.id = $1
ORDER BY
    l2.sort_order DESC,
    l2.id DESC
LIMIT 1;

-- name: UserHasProgramProgress :one
SELECT
    EXISTS (
        SELECT
            1
        FROM
            lms_user_program_progress
        WHERE
            user_id = $1
            AND program_id = $2) AS v;

-- name: UserHasCourseProgress :one
SELECT
    EXISTS (
        SELECT
            1
        FROM
            lms_user_course_progress
        WHERE
            user_id = $1
            AND course_id = $2) AS v;

-- name: UserHasModuleProgress :one
SELECT
    EXISTS (
        SELECT
            1
        FROM
            lms_user_module_progress
        WHERE
            user_id = $1
            AND module_id = $2) AS v;

-- name: UserHasLessonProgress :one
SELECT
    EXISTS (
        SELECT
            1
        FROM
            lms_user_lesson_progress
        WHERE
            user_id = $1
            AND lesson_id = $2) AS v;

-- name: InsertUserLessonProgress :exec
INSERT INTO lms_user_lesson_progress (user_id, lesson_id)
    VALUES ($1, $2)
ON CONFLICT (user_id, lesson_id)
    DO NOTHING;

-- name: HasUserCompletedModule :one
SELECT EXISTS(
    SELECT 1
    FROM lms_user_module_progress
    WHERE user_id = $1
      AND module_id = $2
) AS completed;

-- name: HasUserCompletedCourse :one
SELECT EXISTS(
    SELECT 1
    FROM lms_user_course_progress
    WHERE user_id = $1
      AND course_id = $2
) AS completed;

-- name: HasUserCompletedProgram :one
SELECT EXISTS(
    SELECT 1
    FROM lms_user_program_progress
    WHERE user_id = $1
      AND program_id = $2
) AS completed;

-- name: InsertUserModuleProgress :exec
INSERT INTO lms_user_module_progress (user_id, module_id)
    VALUES ($1, $2)
ON CONFLICT (user_id, module_id)
    DO NOTHING;

-- name: InsertUserCourseProgress :exec
INSERT INTO lms_user_course_progress (user_id, course_id)
    VALUES ($1, $2)
ON CONFLICT (user_id, course_id)
    DO NOTHING;

-- name: InsertUserProgramProgress :exec
INSERT INTO lms_user_program_progress (user_id, program_id)
    VALUES ($1, $2)
ON CONFLICT (user_id, program_id)
    DO NOTHING;

-- name: CountPublishedPracticesInLesson :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
WHERE
    lp.lesson_id = $1
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountUserCompletedPublishedPracticesInLesson :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    INNER JOIN user_practice_progress upp ON upp.question_set_id = lp.question_set_id
WHERE
    lp.lesson_id = $1
    AND upp.user_id = $2
    AND upp.completed_at IS NOT NULL
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountLessonsInModule :one
SELECT
    count(*)::int AS n
FROM
    lessons
WHERE
    module_id = $1
    AND publish_status = 'PUBLISHED';

-- name: CountUserCompletedLessonsInModule :one
SELECT
    count(*)::int AS n
FROM
    lms_user_lesson_progress ulp
    INNER JOIN lessons l ON l.id = ulp.lesson_id
WHERE
    l.module_id = $1
    AND ulp.user_id = $2
    AND l.publish_status = 'PUBLISHED';

-- name: CountModulesInCourse :one
SELECT
    count(*)::int AS n
FROM
    modules
WHERE
    course_id = $1;

-- name: CountUserCompletedModulesInCourse :one
SELECT
    count(*)::int AS n
FROM
    lms_user_module_progress ump
    INNER JOIN modules m ON m.id = ump.module_id
WHERE
    m.course_id = $1
    AND ump.user_id = $2;

-- name: CountCoursesInProgram :one
SELECT
    count(*)::int AS n
FROM
    courses
WHERE
    program_id = $1;

-- name: CountUserCompletedCoursesInProgram :one
SELECT
    count(*)::int AS n
FROM
    lms_user_course_progress ucp
    INNER JOIN courses c ON c.id = ucp.course_id
WHERE
    c.program_id = $1
    AND ucp.user_id = $2;

-- name: ListLMSCompletedLessonIDsByUser :many
SELECT
    lp.lesson_id
FROM
    lms_practices AS lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    LEFT JOIN user_practice_progress upp ON upp.question_set_id = lp.question_set_id
        AND upp.user_id = $1
        AND upp.completed_at IS NOT NULL
WHERE
    lp.lesson_id IS NOT NULL
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED'
GROUP BY
    lp.lesson_id
HAVING
    count(DISTINCT lp.question_set_id) > 0
    AND count(DISTINCT upp.question_set_id) >= count(DISTINCT lp.question_set_id)
ORDER BY
    max(upp.completed_at) ASC,
    lp.lesson_id ASC;

-- name: ListLMSCompletedModuleIDsByUser :many
SELECT
    scoped.module_id
FROM (
    SELECT
        m.id AS module_id,
        lp.question_set_id
    FROM
        modules m
        INNER JOIN lms_practices lp ON (
            lp.module_id = m.id
            OR lp.lesson_id IN (
                SELECT
                    id
                FROM
                    lessons
                WHERE
                    module_id = m.id))
        INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    WHERE
        qs.set_type = 'PRACTICE'
        AND qs.status = 'PUBLISHED'
        AND lp.publish_status = 'PUBLISHED') scoped
    LEFT JOIN user_practice_progress upp ON upp.question_set_id = scoped.question_set_id
        AND upp.user_id = $1
        AND upp.completed_at IS NOT NULL
GROUP BY
    scoped.module_id
HAVING
    count(DISTINCT scoped.question_set_id) > 0
    AND count(DISTINCT upp.question_set_id) >= count(DISTINCT scoped.question_set_id)
ORDER BY
    max(upp.completed_at) ASC,
    scoped.module_id ASC;

-- name: ListLMSCompletedCourseIDsByUser :many
SELECT
    scoped.course_id
FROM (
    SELECT
        c.id AS course_id,
        lp.question_set_id
    FROM
        courses c
        INNER JOIN lms_practices lp ON (
            lp.course_id = c.id
            OR lp.module_id IN (
                SELECT
                    id
                FROM
                    modules
                WHERE
                    course_id = c.id)
            OR lp.lesson_id IN (
                SELECT
                    l.id
                FROM
                    lessons l
                    INNER JOIN modules m ON m.id = l.module_id
                WHERE
                    m.course_id = c.id))
        INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    WHERE
        qs.set_type = 'PRACTICE'
        AND qs.status = 'PUBLISHED'
        AND lp.publish_status = 'PUBLISHED') scoped
    LEFT JOIN user_practice_progress upp ON upp.question_set_id = scoped.question_set_id
        AND upp.user_id = $1
        AND upp.completed_at IS NOT NULL
GROUP BY
    scoped.course_id
HAVING
    count(DISTINCT scoped.question_set_id) > 0
    AND count(DISTINCT upp.question_set_id) >= count(DISTINCT scoped.question_set_id)
ORDER BY
    max(upp.completed_at) ASC,
    scoped.course_id ASC;

-- name: ListLMSCompletedProgramIDsByUser :many
SELECT
    scoped.program_id
FROM (
    SELECT
        c.program_id,
        lp.question_set_id
    FROM
        courses c
        INNER JOIN lms_practices lp ON (
            lp.course_id = c.id
            OR lp.module_id IN (
                SELECT
                    m.id
                FROM
                    modules m
                WHERE
                    m.course_id = c.id)
            OR lp.lesson_id IN (
                SELECT
                    l.id
                FROM
                    lessons l
                    INNER JOIN modules m ON m.id = l.module_id
                WHERE
                    m.course_id = c.id))
        INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    WHERE
        qs.set_type = 'PRACTICE'
        AND qs.status = 'PUBLISHED'
        AND lp.publish_status = 'PUBLISHED') scoped
    LEFT JOIN user_practice_progress upp ON upp.question_set_id = scoped.question_set_id
        AND upp.user_id = $1
        AND upp.completed_at IS NOT NULL
GROUP BY
    scoped.program_id
HAVING
    count(DISTINCT scoped.question_set_id) > 0
    AND count(DISTINCT upp.question_set_id) >= count(DISTINCT scoped.question_set_id)
ORDER BY
    max(upp.completed_at) ASC,
    scoped.program_id ASC;

-- Lesson-based progress within a course (all modules).
-- name: CountLessonsInCourse :one
SELECT
    count(*)::int AS n
FROM
    lessons l
    INNER JOIN modules m ON m.id = l.module_id
WHERE
    m.course_id = $1
    AND l.publish_status = 'PUBLISHED';

-- name: CountUserCompletedLessonsInCourse :one
SELECT
    count(*)::int AS n
FROM
    lms_user_lesson_progress ulp
    INNER JOIN lessons l ON l.id = ulp.lesson_id
    INNER JOIN modules m ON m.id = l.module_id
WHERE
    m.course_id = $1
    AND ulp.user_id = $2
    AND l.publish_status = 'PUBLISHED';

-- Lesson-based progress within a program (all courses).
-- name: CountLessonsInProgram :one
SELECT
    count(*)::int AS n
FROM
    lessons l
    INNER JOIN modules m ON m.id = l.module_id
    INNER JOIN courses c ON c.id = m.course_id
WHERE
    c.program_id = $1
    AND l.publish_status = 'PUBLISHED';

-- name: CountUserCompletedLessonsInProgram :one
SELECT
    count(*)::int AS n
FROM
    lms_user_lesson_progress ulp
    INNER JOIN lessons l ON l.id = ulp.lesson_id
    INNER JOIN modules m ON m.id = l.module_id
    INNER JOIN courses c ON c.id = m.course_id
WHERE
    c.program_id = $1
    AND ulp.user_id = $2
    AND l.publish_status = 'PUBLISHED';

-- Published practices in a module (direct module practices and practices on lessons in the module).
-- name: CountPublishedPracticesInModule :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
WHERE
    (
        lp.module_id = $1
        OR lp.lesson_id IN (
            SELECT
                id
            FROM
                lessons
            WHERE
                module_id = $1))
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountUserCompletedPublishedPracticesInModule :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    INNER JOIN user_practice_progress upp ON upp.question_set_id = lp.question_set_id
WHERE
    (
        lp.module_id = $1
        OR lp.lesson_id IN (
            SELECT
                id
            FROM
                lessons
            WHERE
                module_id = $1))
    AND upp.user_id = $2
    AND upp.completed_at IS NOT NULL
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountPublishedPracticesInCourse :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
WHERE
    (
        lp.course_id = $1
        OR lp.module_id IN (
            SELECT
                id
            FROM
                modules
            WHERE
                course_id = $1)
        OR lp.lesson_id IN (
            SELECT
                l.id
            FROM
                lessons l
                INNER JOIN modules m ON m.id = l.module_id
            WHERE
                m.course_id = $1))
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountUserCompletedPublishedPracticesInCourse :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    INNER JOIN user_practice_progress upp ON upp.question_set_id = lp.question_set_id
WHERE
    (
        lp.course_id = $1
        OR lp.module_id IN (
            SELECT
                id
            FROM
                modules
            WHERE
                course_id = $1)
        OR lp.lesson_id IN (
            SELECT
                l.id
            FROM
                lessons l
                INNER JOIN modules m ON m.id = l.module_id
            WHERE
                m.course_id = $1))
    AND upp.user_id = $2
    AND upp.completed_at IS NOT NULL
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountPublishedPracticesInProgram :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
WHERE
    (
        lp.course_id IN (
            SELECT
                c.id
            FROM
                courses c
            WHERE
                c.program_id = $1)
        OR lp.module_id IN (
            SELECT
                m.id
            FROM
                modules m
                INNER JOIN courses c ON c.id = m.course_id
            WHERE
                c.program_id = $1)
        OR lp.lesson_id IN (
            SELECT
                l.id
            FROM
                lessons l
                INNER JOIN modules m ON m.id = l.module_id
                INNER JOIN courses c ON c.id = m.course_id
            WHERE
                c.program_id = $1))
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountUserCompletedPublishedPracticesInProgram :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    INNER JOIN user_practice_progress upp ON upp.question_set_id = lp.question_set_id
WHERE
    (
        lp.course_id IN (
            SELECT
                c.id
            FROM
                courses c
            WHERE
                c.program_id = $1)
        OR lp.module_id IN (
            SELECT
                m.id
            FROM
                modules m
                INNER JOIN courses c ON c.id = m.course_id
            WHERE
                c.program_id = $1)
        OR lp.lesson_id IN (
            SELECT
                l.id
            FROM
                lessons l
                INNER JOIN modules m ON m.id = l.module_id
                INNER JOIN courses c ON c.id = m.course_id
            WHERE
                c.program_id = $1))
    AND upp.user_id = $2
    AND upp.completed_at IS NOT NULL
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- Published practices directly attached to module_id (not via lesson_id).
-- name: CountPublishedDirectPracticesInModule :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
WHERE
    lp.module_id = $1
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountUserCompletedPublishedDirectPracticesInModule :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    INNER JOIN user_practice_progress upp ON upp.question_set_id = lp.question_set_id
WHERE
    lp.module_id = $1
    AND upp.user_id = $2
    AND upp.completed_at IS NOT NULL
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- Published practices directly attached to course_id (not via module_id/lesson_id).
-- name: CountPublishedDirectPracticesInCourse :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
WHERE
    lp.course_id = $1
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: CountUserCompletedPublishedDirectPracticesInCourse :one
SELECT
    count(*)::int AS n
FROM
    lms_practices lp
    INNER JOIN question_sets qs ON qs.id = lp.question_set_id
    INNER JOIN user_practice_progress upp ON upp.question_set_id = lp.question_set_id
WHERE
    lp.course_id = $1
    AND upp.user_id = $2
    AND upp.completed_at IS NOT NULL
    AND qs.set_type = 'PRACTICE'
    AND qs.status = 'PUBLISHED'
    AND lp.publish_status = 'PUBLISHED';

-- name: GetPracticeScopeByQuestionSetID :one
SELECT
    id,
    course_id,
    module_id,
    lesson_id
FROM
    lms_practices
WHERE
    question_set_id = $1
ORDER BY
    id DESC
LIMIT 1;
