<?php
declare(strict_types=1);

/**
 * EnglishSpeak - Lesson Progress Engine
 *
 * Tính tiến độ Lesson từ:
 * - activities: danh sách Activity đang bật trong Lesson
 * - student_content_progress: tiến độ Activity của học sinh
 *
 * Không lưu bảng tổng hợp riêng để tránh dữ liệu bị lệch.
 */
function englishSpeakTableColumns(PDO $pdo, string $table): array
{
    $stmt = $pdo->prepare(
        "SELECT COUNT(*)
         FROM information_schema.TABLES
         WHERE TABLE_SCHEMA = DATABASE()
           AND TABLE_NAME = ?"
    );
    $stmt->execute([$table]);

    if ((int)$stmt->fetchColumn() === 0) {
        return [];
    }

    $rows = $pdo
        ->query("SHOW COLUMNS FROM `{$table}`")
        ->fetchAll(PDO::FETCH_ASSOC);

    return array_column($rows, null, 'Field');
}

function englishSpeakFirstExistingColumn(
    array $columns,
    array $candidates
): ?string {
    foreach ($candidates as $candidate) {
        if (isset($columns[$candidate])) {
            return $candidate;
        }
    }

    return null;
}

/**
 * Trả về tiến độ của một Lesson.
 *
 * @return array{
 *   lesson_id:int,
 *   total_activities:int,
 *   completed_activities:int,
 *   progress_percent:int,
 *   lesson_completed:bool,
 *   activity_ids:int[],
 *   completed_activity_ids:int[]
 * }
 */
function englishSpeakGetLessonProgress(
    PDO $pdo,
    int $studentId,
    int $lessonId
): array {
    if ($studentId <= 0) {
        throw new InvalidArgumentException('Invalid student_id.');
    }

    if ($lessonId <= 0) {
        throw new InvalidArgumentException('Invalid lesson_id.');
    }

    $activityColumns = englishSpeakTableColumns($pdo, 'activities');
    $progressColumns = englishSpeakTableColumns(
        $pdo,
        'student_content_progress'
    );

    if ($activityColumns === []) {
        throw new RuntimeException('Missing activities table.');
    }

    if ($progressColumns === []) {
        throw new RuntimeException(
            'Missing student_content_progress table.'
        );
    }

    foreach (
        ['student_id', 'content_type', 'content_id', 'status']
        as $requiredColumn
    ) {
        if (!isset($progressColumns[$requiredColumn])) {
            throw new RuntimeException(
                "Missing student_content_progress.{$requiredColumn}."
            );
        }
    }

    if (!isset($activityColumns['lesson_id'])) {
        throw new RuntimeException('Missing activities.lesson_id.');
    }

    $statusColumn = englishSpeakFirstExistingColumn(
        $activityColumns,
        ['status']
    );

    $sortColumn = englishSpeakFirstExistingColumn(
        $activityColumns,
        ['sort_order', 'display_order', 'position', 'order_index']
    );

    $where = ['lesson_id = ?'];
    $params = [$lessonId];

    /*
     * Chỉ tính các Activity đang dùng trong Lesson.
     * Nếu bảng chưa có status thì tính toàn bộ.
     */
    if ($statusColumn !== null) {
        $where[] =
            "LOWER(TRIM(`{$statusColumn}`)) "
            . "IN ('active','published','ready')";
    }

    $orderSql = $sortColumn !== null
        ? "`{$sortColumn}` ASC, id ASC"
        : 'id ASC';

    $activityStmt = $pdo->prepare(
        'SELECT id
         FROM activities
         WHERE ' . implode(' AND ', $where) . '
         ORDER BY ' . $orderSql
    );
    $activityStmt->execute($params);

    $activityIds = array_map(
        'intval',
        $activityStmt->fetchAll(PDO::FETCH_COLUMN)
    );

    $totalActivities = count($activityIds);

    if ($totalActivities === 0) {
        return [
            'lesson_id' => $lessonId,
            'total_activities' => 0,
            'completed_activities' => 0,
            'progress_percent' => 0,
            'lesson_completed' => false,
            'activity_ids' => [],
            'completed_activity_ids' => [],
        ];
    }

    $placeholders = implode(
        ',',
        array_fill(0, $totalActivities, '?')
    );

    $progressStmt = $pdo->prepare(
        "SELECT content_id
         FROM student_content_progress
         WHERE student_id = ?
           AND content_type = 'activity'
           AND content_id IN ({$placeholders})
           AND LOWER(TRIM(status)) = 'completed'"
    );

    $progressStmt->execute(
        array_merge([$studentId], $activityIds)
    );

    $completedActivityIds = array_values(
        array_unique(
            array_map(
                'intval',
                $progressStmt->fetchAll(PDO::FETCH_COLUMN)
            )
        )
    );

    $completedActivities = count($completedActivityIds);
    $progressPercent = (int)round(
        ($completedActivities / $totalActivities) * 100
    );

    return [
        'lesson_id' => $lessonId,
        'total_activities' => $totalActivities,
        'completed_activities' => $completedActivities,
        'progress_percent' => min(100, max(0, $progressPercent)),
        'lesson_completed' =>
            $totalActivities > 0
            && $completedActivities >= $totalActivities,
        'activity_ids' => $activityIds,
        'completed_activity_ids' => $completedActivityIds,
    ];
}

/**
 * Tìm Lesson của Activity rồi trả về tiến độ Lesson.
 */
function englishSpeakGetLessonProgressByActivity(
    PDO $pdo,
    int $studentId,
    int $activityId
): array {
    if ($activityId <= 0) {
        throw new InvalidArgumentException('Invalid activity_id.');
    }

    $stmt = $pdo->prepare(
        'SELECT lesson_id
         FROM activities
         WHERE id = ?
         LIMIT 1'
    );
    $stmt->execute([$activityId]);

    $lessonId = (int)($stmt->fetchColumn() ?: 0);

    if ($lessonId <= 0) {
        throw new RuntimeException(
            'Could not determine the Lesson of this Activity.'
        );
    }

    return englishSpeakGetLessonProgress(
        $pdo,
        $studentId,
        $lessonId
    );
}


/**
 * Lưu một dòng tổng hợp Lesson vào student_content_progress.
 *
 * content_type = 'lesson'
 * best_score = % tiến độ Lesson
 * xp_earned = 0 (Lesson không tự cộng XP ở Engine này)
 *
 * Lesson đã completed thì không bao giờ bị giảm trạng thái hoặc điểm.
 */
function englishSpeakSyncLessonProgress(
    PDO $pdo,
    int $studentId,
    int $lessonId
): array {
    $summary = englishSpeakGetLessonProgress(
        $pdo,
        $studentId,
        $lessonId
    );

    $status = $summary['lesson_completed']
        ? 'completed'
        : (
            $summary['completed_activities'] > 0
                ? 'in_progress'
                : 'not_started'
        );

    $existingStmt = $pdo->prepare(
        "SELECT id, status, best_score
         FROM student_content_progress
         WHERE student_id = ?
           AND content_type = 'lesson'
           AND content_id = ?
         LIMIT 1"
    );
    $existingStmt->execute([$studentId, $lessonId]);
    $existing = $existingStmt->fetch(PDO::FETCH_ASSOC);

    if ($existing) {
        $oldStatus = strtolower(
            trim((string)($existing['status'] ?? ''))
        );
        /*
         * Trạng thái Lesson phải luôn phản ánh tiến độ Activity hiện tại.
         * Không giữ 'completed' cũ nếu Lesson hiện chưa hoàn thành tất cả
         * Activity active (ví dụ giáo viên thêm Activity mới vào Lesson).
         */
        $finalStatus = $status;

        /*
         * best_score của dòng lesson được dùng làm % tiến độ hiện tại,
         * không phải "best score" lịch sử. Nhờ đó 2/5 Activity sẽ là 40%,
         * không bị giữ 100% từ dữ liệu cũ.
         */
        $finalBest = (int)$summary['progress_percent'];

        $updateStmt = $pdo->prepare(
            "UPDATE student_content_progress
             SET status = ?,
                 best_score = ?,
                 xp_earned = 0,
                 last_attempt_at = NOW(),
                 completed_at = CASE
                    WHEN ? = 'completed'
                    THEN COALESCE(completed_at, NOW())
                    ELSE completed_at
                 END
             WHERE id = ?"
        );
        $updateStmt->execute([
            $finalStatus,
            $finalBest,
            $finalStatus,
            (int)$existing['id'],
        ]);
    } else {
        $insertStmt = $pdo->prepare(
            "INSERT INTO student_content_progress (
                student_id,
                content_type,
                content_id,
                status,
                best_score,
                attempt_count,
                xp_earned,
                first_started_at,
                completed_at,
                last_attempt_at
             ) VALUES (
                ?,
                'lesson',
                ?,
                ?,
                ?,
                1,
                0,
                NOW(),
                CASE WHEN ? = 'completed' THEN NOW() ELSE NULL END,
                NOW()
             )"
        );
        $insertStmt->execute([
            $studentId,
            $lessonId,
            $status,
            (int)$summary['progress_percent'],
            $status,
        ]);
    }

    /*
     * Trả về đúng trạng thái vừa đồng bộ.
     */
    $summary['status'] = $status;

    return $summary;
}

/**
 * Trả về trạng thái mở khóa của các Lesson đang active trong một Unit.
 *
 * Quy tắc:
 * - Lesson đầu tiên luôn mở.
 * - Lesson kế tiếp mở khi Lesson ngay trước đã completed.
 * - Lesson đã từng bắt đầu/completed luôn giữ mở, không relock.
 */
function englishSpeakGetUnitLessonStates(
    PDO $pdo,
    int $studentId,
    int $unitId
): array {
    if ($studentId <= 0 || $unitId <= 0) {
        return [];
    }

    $lessonColumns = englishSpeakTableColumns($pdo, 'lessons');
    if ($lessonColumns === []) {
        throw new RuntimeException('Missing lessons table.');
    }

    $sortColumn = englishSpeakFirstExistingColumn(
        $lessonColumns,
        ['sort_order', 'display_order', 'position', 'order_index']
    );
    $orderSql = $sortColumn !== null
        ? "`{$sortColumn}` ASC, id ASC"
        : 'id ASC';

    $where = ['unit_id = ?'];
    if (isset($lessonColumns['status'])) {
        $where[] =
            "LOWER(TRIM(status)) IN ('active','published','ready')";
    }

    $lessonStmt = $pdo->prepare(
        'SELECT id
         FROM lessons
         WHERE ' . implode(' AND ', $where) . '
         ORDER BY ' . $orderSql
    );
    $lessonStmt->execute([$unitId]);
    $lessonIds = array_map(
        'intval',
        $lessonStmt->fetchAll(PDO::FETCH_COLUMN)
    );

    if ($lessonIds === []) {
        return [];
    }

    /*
     * Đồng bộ dữ liệu tổng hợp cho dữ liệu Activity cũ.
     * Nhờ đó các Lesson đã học trước khi có Engine vẫn được nhận diện.
     */
    foreach ($lessonIds as $lessonId) {
        englishSpeakSyncLessonProgress(
            $pdo,
            $studentId,
            $lessonId
        );
    }

    $placeholders = implode(
        ',',
        array_fill(0, count($lessonIds), '?')
    );

    $progressStmt = $pdo->prepare(
        "SELECT content_id, status, best_score
         FROM student_content_progress
         WHERE student_id = ?
           AND content_type = 'lesson'
           AND content_id IN ({$placeholders})"
    );
    $progressStmt->execute(
        array_merge([$studentId], $lessonIds)
    );

    $progressByLesson = [];
    foreach ($progressStmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
        $progressByLesson[(int)$row['content_id']] = [
            'status' => strtolower(
                trim((string)($row['status'] ?? 'not_started'))
            ),
            'progress_percent' => max(
                0,
                min(100, (int)($row['best_score'] ?? 0))
            ),
        ];
    }

    $states = [];
    $previousCompleted = false;

    foreach ($lessonIds as $index => $lessonId) {
        $progress = $progressByLesson[$lessonId] ?? [
            'status' => 'not_started',
            'progress_percent' => 0,
        ];

        $hasOwnHistory =
            $progress['status'] !== 'not_started'
            || $progress['progress_percent'] > 0;

        $unlocked =
            $index === 0
            || $previousCompleted
            || $hasOwnHistory;

        $states[$lessonId] = [
            'lesson_id' => $lessonId,
            'unlocked' => $unlocked,
            'status' => $progress['status'],
            'progress_percent' =>
                (int)$progress['progress_percent'],
        ];

        $previousCompleted =
            $progress['status'] === 'completed';
    }

    return $states;
}

/**
 * Kiểm tra một Lesson có đang mở với học sinh hay không.
 */
function englishSpeakIsLessonUnlocked(
    PDO $pdo,
    int $studentId,
    int $lessonId
): bool {
    $stmt = $pdo->prepare(
        'SELECT unit_id
         FROM lessons
         WHERE id = ?
         LIMIT 1'
    );
    $stmt->execute([$lessonId]);
    $unitId = (int)($stmt->fetchColumn() ?: 0);

    if ($unitId <= 0) {
        return false;
    }

    $states = englishSpeakGetUnitLessonStates(
        $pdo,
        $studentId,
        $unitId
    );

    return (bool)($states[$lessonId]['unlocked'] ?? false);
}
