<?php

/**
 * Program session definitions: ordered visit slots with assigned questionnaires.
 */

const QDB_PROGRAM_SESSIONS_AUTO_REPEAT_KEY = 'program_sessions_auto_repeat';

/**
 * @return array{autoRepeatSessions: bool}
 */
function qdb_program_session_settings_get(PDO $pdo): array
{
    require_once __DIR__ . '/settings.php';
    qdb_ensure_system_setting_table($pdo);
    $stmt = $pdo->prepare(
        'SELECT settingValue FROM system_setting WHERE settingKey = :k'
    );
    $stmt->execute([':k' => QDB_PROGRAM_SESSIONS_AUTO_REPEAT_KEY]);
    $val = $stmt->fetchColumn();
    return [
        'autoRepeatSessions' => $val !== false && (int)$val === 1,
    ];
}

/**
 * @param array{autoRepeatSessions?: bool} $partial
 * @return array{autoRepeatSessions: bool}
 */
function qdb_program_session_settings_save(PDO $pdo, array $partial): array
{
    $current = qdb_program_session_settings_get($pdo);
    if (array_key_exists('autoRepeatSessions', $partial)) {
        $current['autoRepeatSessions'] = !empty($partial['autoRepeatSessions']);
    }
    require_once __DIR__ . '/settings.php';
    qdb_ensure_system_setting_table($pdo);
    $stmt = $pdo->prepare(
        'INSERT INTO system_setting (settingKey, settingValue)
         VALUES (:k, :v)
         ON CONFLICT(settingKey) DO UPDATE SET settingValue = excluded.settingValue'
    );
    $stmt->execute([
        ':k' => QDB_PROGRAM_SESSIONS_AUTO_REPEAT_KEY,
        ':v' => $current['autoRepeatSessions'] ? '1' : '0',
    ]);
    return $current;
}

/**
 * @return list<array<string, mixed>>
 */
function qdb_program_session_questionnaires(PDO $pdo, string $sessionID): array {
    $stmt = $pdo->prepare(
        'SELECT psq.questionnaireID, psq.orderIndex, q.name
         FROM program_session_questionnaire psq
         LEFT JOIN questionnaire q ON q.questionnaireID = psq.questionnaireID
         WHERE psq.sessionID = :sid
         ORDER BY psq.orderIndex, psq.questionnaireID'
    );
    $stmt->execute([':sid' => $sessionID]);
    $rows = [];
    foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
        $rows[] = [
            'questionnaireID' => (string)$row['questionnaireID'],
            'name'            => (string)($row['name'] ?? ''),
            'orderIndex'      => (int)$row['orderIndex'],
        ];
    }
    return $rows;
}

/**
 * @return list<array<string, mixed>>
 */
function qdb_list_program_sessions(PDO $pdo, bool $activeOnly = false): array {
    $sql = 'SELECT sessionID, name, description, sessionNumber, orderIndex, isActive, createdAt, updatedAt
            FROM program_session';
    if ($activeOnly) {
        $sql .= ' WHERE isActive = 1';
    }
    $sql .= ' ORDER BY orderIndex, sessionNumber, name';
    $stmt = $pdo->query($sql);
    $sessions = [];
    foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
        $row['isActive'] = (int)$row['isActive'];
        $row['sessionNumber'] = (int)$row['sessionNumber'];
        $row['orderIndex'] = (int)$row['orderIndex'];
        $row['questionnaires'] = qdb_program_session_questionnaires($pdo, $row['sessionID']);
        $sessions[] = $row;
    }
    return $sessions;
}

function qdb_get_program_session(PDO $pdo, string $sessionID): ?array {
    $stmt = $pdo->prepare(
        'SELECT sessionID, name, description, sessionNumber, orderIndex, isActive, createdAt, updatedAt
         FROM program_session WHERE sessionID = :sid'
    );
    $stmt->execute([':sid' => $sessionID]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$row) {
        return null;
    }
    $row['isActive'] = (int)$row['isActive'];
    $row['sessionNumber'] = (int)$row['sessionNumber'];
    $row['orderIndex'] = (int)$row['orderIndex'];
    $row['questionnaires'] = qdb_program_session_questionnaires($pdo, $sessionID);
    return $row;
}

/**
 * sessionNumber (1-based visit index) and orderIndex (0-based sort key) stay in sync:
 * orderIndex = sessionNumber - 1.
 *
 * @param array<string, mixed> $body
 * @param array<string, mixed>|null $existing
 * @return array{sessionNumber: int, orderIndex: int}
 */
function qdb_program_session_resolve_order(PDO $pdo, array $body, ?array $existing = null): array
{
    if (isset($body['sessionNumber'])) {
        $sessionNumber = max(1, (int)$body['sessionNumber']);
        return [
            'sessionNumber' => $sessionNumber,
            'orderIndex'    => $sessionNumber - 1,
        ];
    }
    if (isset($body['orderIndex'])) {
        $orderIndex = max(0, (int)$body['orderIndex']);
        return [
            'sessionNumber' => $orderIndex + 1,
            'orderIndex'    => $orderIndex,
        ];
    }
    if ($existing !== null) {
        $sessionNumber = max(1, (int)$existing['sessionNumber']);
        return [
            'sessionNumber' => $sessionNumber,
            'orderIndex'    => $sessionNumber - 1,
        ];
    }

    $maxNum = (int)$pdo->query('SELECT COALESCE(MAX(sessionNumber), 0) FROM program_session')->fetchColumn();
    $maxOrd = (int)$pdo->query('SELECT COALESCE(MAX(orderIndex), -1) FROM program_session')->fetchColumn();
    $sessionNumber = max($maxNum, $maxOrd + 1, 0) + 1;

    return [
        'sessionNumber' => $sessionNumber,
        'orderIndex'    => $sessionNumber - 1,
    ];
}

/**
 * @return list<array{questionnaireID: string, orderIndex: int}>
 */
function qdb_parse_program_session_questionnaires(array $rows): array {
    $out = [];
    $idx = 0;
    foreach ($rows as $row) {
        if (!is_array($row)) {
            continue;
        }
        $qnID = trim((string)($row['questionnaireID'] ?? ''));
        if ($qnID === '') {
            continue;
        }
        $out[] = [
            'questionnaireID' => $qnID,
            'orderIndex'      => (int)($row['orderIndex'] ?? $idx),
        ];
        $idx++;
    }
    return $out;
}

function qdb_sync_program_session_questionnaires(PDO $pdo, string $sessionID, array $members): void {
    $pdo->prepare('DELETE FROM program_session_questionnaire WHERE sessionID = :sid')
        ->execute([':sid' => $sessionID]);
    if ($members === []) {
        return;
    }
    $ins = $pdo->prepare(
        'INSERT INTO program_session_questionnaire (sessionID, questionnaireID, orderIndex)
         VALUES (:sid, :qn, :o)'
    );
    foreach ($members as $m) {
        $ins->execute([
            ':sid' => $sessionID,
            ':qn'  => $m['questionnaireID'],
            ':o'   => $m['orderIndex'],
        ]);
    }
}

/**
 * @param list<array{questionnaireID: string, orderIndex: int}> $members
 */
function qdb_validate_program_session_questionnaires(PDO $pdo, array $members): ?string {
    if ($members === []) {
        return null;
    }
    $chk = $pdo->prepare('SELECT 1 FROM questionnaire WHERE questionnaireID = :id');
    foreach ($members as $m) {
        $chk->execute([':id' => $m['questionnaireID']]);
        if (!$chk->fetch()) {
            return 'Unknown questionnaire: ' . $m['questionnaireID'];
        }
    }
    return null;
}

/**
 * @return array{cycleNumber: int, cycleStartedAt: int, updatedAt: int}
 */
function qdb_client_program_cycle_get(PDO $pdo, string $clientCode, bool $create = true): array
{
    $stmt = $pdo->prepare(
        'SELECT cycleNumber, cycleStartedAt, updatedAt FROM client_program_cycle WHERE clientCode = :cc'
    );
    $stmt->execute([':cc' => $clientCode]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if ($row) {
        return [
            'cycleNumber'    => max(1, (int)$row['cycleNumber']),
            'cycleStartedAt' => (int)$row['cycleStartedAt'],
            'updatedAt'      => (int)$row['updatedAt'],
        ];
    }
    if (!$create) {
        return ['cycleNumber' => 1, 'cycleStartedAt' => 0, 'updatedAt' => 0];
    }
    $now = time();
    $pdo->prepare(
        'INSERT INTO client_program_cycle (clientCode, cycleNumber, cycleStartedAt, updatedAt)
         VALUES (:cc, 1, 0, :t)'
    )->execute([':cc' => $clientCode, ':t' => $now]);
    return ['cycleNumber' => 1, 'cycleStartedAt' => 0, 'updatedAt' => $now];
}

function qdb_program_session_is_complete_for_client(
    PDO $pdo,
    string $clientCode,
    array $session,
    int $cycleStartedAt,
): bool {
    $questionnaires = $session['questionnaires'] ?? [];
    if ($questionnaires === []) {
        return true;
    }
    if ($cycleStartedAt <= 0) {
        $stmt = $pdo->prepare(
            "SELECT 1 FROM completed_questionnaire
             WHERE clientCode = :cc AND questionnaireID = :qn AND status = 'completed'
             LIMIT 1"
        );
        foreach ($questionnaires as $q) {
            $stmt->execute([
                ':cc' => $clientCode,
                ':qn' => $q['questionnaireID'],
            ]);
            if (!$stmt->fetch()) {
                return false;
            }
        }
        return true;
    }
    $stmt = $pdo->prepare(
        "SELECT 1 FROM completed_questionnaire
         WHERE clientCode = :cc AND questionnaireID = :qn AND status = 'completed'
           AND completedAt > :cycleStart
         LIMIT 1"
    );
    foreach ($questionnaires as $q) {
        $stmt->execute([
            ':cc'         => $clientCode,
            ':qn'         => $q['questionnaireID'],
            ':cycleStart' => $cycleStartedAt,
        ]);
        if (!$stmt->fetch()) {
            return false;
        }
    }
    return true;
}

/**
 * @return array<string, mixed>
 */
function qdb_client_program_progress(PDO $pdo, string $clientCode): array
{
    $settings = qdb_program_session_settings_get($pdo);
    $sessions = qdb_list_program_sessions($pdo, true);
    $cycle = qdb_client_program_cycle_get($pdo, $clientCode);
    $totalSessions = count($sessions);

    if ($totalSessions === 0) {
        return [
            'cycleNumber'              => $cycle['cycleNumber'],
            'cycleStartedAt'             => (int)$cycle['cycleStartedAt'],
            'totalSessions'              => 0,
            'sessionsCompletedInCycle'   => 0,
            'currentSessionID'           => null,
            'currentSessionNumber'       => null,
            'currentSessionName'         => null,
            'cycleComplete'              => true,
            'programComplete'            => true,
            'autoRepeatSessions'         => $settings['autoRepeatSessions'],
        ];
    }

    $completedCount = 0;
    $currentSession = null;
    foreach ($sessions as $session) {
        if (!qdb_program_session_is_complete_for_client(
            $pdo,
            $clientCode,
            $session,
            $cycle['cycleStartedAt'],
        )) {
            $currentSession = $session;
            break;
        }
        $completedCount++;
    }

    $allComplete = $currentSession === null;
    $programComplete = $allComplete && !$settings['autoRepeatSessions'];

    return [
        'cycleNumber'              => $cycle['cycleNumber'],
        'cycleStartedAt'           => (int)$cycle['cycleStartedAt'],
        'totalSessions'            => $totalSessions,
        'sessionsCompletedInCycle' => $completedCount,
        'currentSessionID'         => $currentSession['sessionID'] ?? null,
        'currentSessionNumber'     => isset($currentSession['sessionNumber'])
            ? (int)$currentSession['sessionNumber']
            : null,
        'currentSessionName'       => $currentSession['name'] ?? null,
        'cycleComplete'            => $allComplete,
        'programComplete'          => $programComplete,
        'autoRepeatSessions'       => $settings['autoRepeatSessions'],
    ];
}

/**
 * When auto-repeat is enabled and every active session is complete in the current cycle,
 * advance to the next cycle so counselors start again at session 1.
 */
function qdb_maybe_advance_program_cycle(PDO $pdo, string $clientCode): bool
{
    $settings = qdb_program_session_settings_get($pdo);
    if (!$settings['autoRepeatSessions']) {
        return false;
    }

    $sessions = qdb_list_program_sessions($pdo, true);
    if ($sessions === []) {
        return false;
    }

    $cycle = qdb_client_program_cycle_get($pdo, $clientCode);
    foreach ($sessions as $session) {
        if (!qdb_program_session_is_complete_for_client(
            $pdo,
            $clientCode,
            $session,
            $cycle['cycleStartedAt'],
        )) {
            return false;
        }
    }

    $now = time();
    $pdo->prepare(
        'UPDATE client_program_cycle
         SET cycleNumber = cycleNumber + 1, cycleStartedAt = :t, updatedAt = :t
         WHERE clientCode = :cc'
    )->execute([':cc' => $clientCode, ':t' => $now]);

    return true;
}

/**
 * Mobile-friendly session list (active only, no admin timestamps).
 *
 * @return array{sessions: list<array<string, mixed>>, settings: array{autoRepeatSessions: bool}}
 */
function qdb_app_program_sessions_payload(PDO $pdo): array
{
    $settings = qdb_program_session_settings_get($pdo);
    $sessions = qdb_list_program_sessions($pdo, true);
    $out = [];
    foreach ($sessions as $s) {
        $out[] = [
            'sessionID'     => $s['sessionID'],
            'name'          => $s['name'],
            'description'   => $s['description'],
            'sessionNumber' => $s['sessionNumber'],
            'orderIndex'    => $s['orderIndex'],
            'questionnaires' => array_map(static function (array $q): array {
                return [
                    'questionnaireID' => $q['questionnaireID'],
                    'orderIndex'      => $q['orderIndex'],
                ];
            }, $s['questionnaires']),
        ];
    }
    return [
        'sessions' => $out,
        'settings' => $settings,
    ];
}

/** @deprecated Use qdb_app_program_sessions_payload() */
function qdb_app_program_sessions(PDO $pdo): array
{
    return qdb_app_program_sessions_payload($pdo)['sessions'];
}
