<?php
/**
 * Analytics queries for Insights dashboard (RBAC-scoped).
 */

/**
 * Daily upload counts for the last N calendar days (zeros for quiet days).
 *
 * @return list<array{date: string, count: int}>
 */
function qdb_analytics_submissions_by_day(PDO $pdo, array $tokenRec, int $days = 14): array {
    $days = max(1, min(90, $days));
    [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl');

    $startTs = strtotime('today', time()) - ($days - 1) * 86400;

    $stmt = $pdo->prepare(
        "SELECT qs.submittedAt
         FROM questionnaire_submission qs
         INNER JOIN client cl ON cl.clientCode = qs.clientCode
         WHERE $rbacClause AND qs.submittedAt >= :start"
    );
    $stmt->execute(array_merge($rbacParams, [':start' => $startTs]));
    $byDate = [];
    foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $submittedAt) {
        $key = date('Y-m-d', (int)$submittedAt);
        $byDate[$key] = ($byDate[$key] ?? 0) + 1;
    }

    $out = [];
    for ($i = 0; $i < $days; $i++) {
        $key = date('Y-m-d', $startTs + $i * 86400);
        $out[] = ['date' => $key, 'count' => $byDate[$key] ?? 0];
    }
    return $out;
}

function qdb_analytics_overview(PDO $pdo, array $tokenRec): array {
    [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl');

    $stmt = $pdo->prepare("SELECT COUNT(*) FROM client cl WHERE $rbacClause");
    $stmt->execute($rbacParams);
    $clientCount = (int)$stmt->fetchColumn();

    $stmt = $pdo->prepare(
        "SELECT COUNT(DISTINCT cq.clientCode) FROM completed_questionnaire cq
         INNER JOIN client cl ON cl.clientCode = cq.clientCode
         WHERE $rbacClause"
    );
    $stmt->execute($rbacParams);
    $clientsWithCompletions = (int)$stmt->fetchColumn();

    $now = time();
    $d7 = $now - 7 * 86400;
    $d30 = $now - 30 * 86400;

    $stmt = $pdo->prepare(
        "SELECT COUNT(*) FROM questionnaire_submission qs
         INNER JOIN client cl ON cl.clientCode = qs.clientCode
         WHERE $rbacClause AND qs.submittedAt >= :t"
    );
    $stmt->execute(array_merge($rbacParams, [':t' => $d7]));
    $submissions7d = (int)$stmt->fetchColumn();

    $stmt = $pdo->prepare(
        "SELECT COUNT(*) FROM questionnaire_submission qs
         INNER JOIN client cl ON cl.clientCode = qs.clientCode
         WHERE $rbacClause AND qs.submittedAt >= :t"
    );
    $stmt->execute(array_merge($rbacParams, [':t' => $d30]));
    $submissions30d = (int)$stmt->fetchColumn();

    $qnRows = $pdo->query(
        "SELECT questionnaireID, name, orderIndex FROM questionnaire WHERE state = 'active' ORDER BY orderIndex, name"
    )->fetchAll(PDO::FETCH_ASSOC);

    $perQuestionnaire = [];
    foreach ($qnRows as $qn) {
        $qnID = $qn['questionnaireID'];
        $stmt = $pdo->prepare(
            "SELECT COUNT(DISTINCT cq.clientCode) FROM completed_questionnaire cq
             INNER JOIN client cl ON cl.clientCode = cq.clientCode
             WHERE cq.questionnaireID = :qn AND $rbacClause"
        );
        $stmt->execute(array_merge([':qn' => $qnID], $rbacParams));
        $completed = (int)$stmt->fetchColumn();
        $ratio = $clientCount > 0 ? round(100 * $completed / $clientCount, 1) : 0;
        $perQuestionnaire[] = [
            'questionnaireID' => $qnID,
            'name'            => $qn['name'],
            'completedCount'  => $completed,
            'eligibleCount'   => $clientCount,
            'completionRatio' => $ratio,
        ];
    }

    return [
        'clientCount'            => $clientCount,
        'clientsWithCompletions' => $clientsWithCompletions,
        'submissionsLast7d'      => $submissions7d,
        'submissionsLast30d'     => $submissions30d,
        'questionnaires'         => $perQuestionnaire,
        'submissionsByDay'       => qdb_analytics_submissions_by_day($pdo, $tokenRec, 14),
        'scoringProfiles'        => qdb_analytics_scoring_profiles($pdo, $tokenRec),
    ];
}

/**
 * Band distribution and averages per active scoring profile (RBAC-scoped).
 *
 * @return list<array<string, mixed>>
 */
function qdb_analytics_scoring_profiles(PDO $pdo, array $tokenRec): array {
    require_once __DIR__ . '/scoring.php';
    [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl');

    $profiles = $pdo->query(
        "SELECT profileID, name, description,
                greenMin, greenMax, yellowMin, yellowMax, redMin, isActive
         FROM scoring_profile WHERE isActive = 1 ORDER BY name"
    )->fetchAll(PDO::FETCH_ASSOC);

    $out = [];
    foreach ($profiles as $p) {
        $profileID = $p['profileID'];
        $stmt = $pdo->prepare(
            "SELECT r.band, r.coachBand, r.weightedTotal
             FROM client_scoring_profile_result r
             INNER JOIN client cl ON cl.clientCode = r.clientCode
             WHERE r.profileID = :pid AND ($rbacClause)"
        );
        $stmt->execute(array_merge([':pid' => $profileID], $rbacParams));
        $computedBands = ['green' => 0, 'yellow' => 0, 'red' => 0];
        $coachBands = ['green' => 0, 'yellow' => 0, 'red' => 0];
        $pendingReview = 0;
        $totalSum = 0.0;
        $count = 0;
        foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $calcBand = $row['band'] ?? '';
            if (isset($computedBands[$calcBand])) {
                $computedBands[$calcBand]++;
            }
            $coachBand = trim((string)($row['coachBand'] ?? ''));
            if ($coachBand === '') {
                $pendingReview++;
            } elseif (isset($coachBands[$coachBand])) {
                $coachBands[$coachBand]++;
            }
            $totalSum += (float)$row['weightedTotal'];
            $count++;
        }
        $members = qdb_scoring_profile_members($pdo, $profileID);
        $bandRanges = qdb_normalize_scoring_bands($p);
        $out[] = [
            'profileID'   => $profileID,
            'name'        => $p['name'],
            'description' => $p['description'] ?? '',
            'greenMin'    => $bandRanges['greenMin'],
            'greenMax'    => $bandRanges['greenMax'],
            'yellowMin'   => $bandRanges['yellowMin'],
            'yellowMax'   => $bandRanges['yellowMax'],
            'redMin'      => $bandRanges['redMin'],
            'questionnaires' => $members,
            'clientCount'    => $count,
            'bands'          => $computedBands,
            'computedBands'  => $computedBands,
            'coachBands'     => $coachBands,
            'pendingReview'  => $pendingReview,
            'averageTotal'   => $count > 0 ? round($totalSum / $count, 2) : null,
        ];
    }
    return $out;
}

function qdb_analytics_stale_clients(PDO $pdo, array $tokenRec): array {
    [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl');
    $now = time();

    $sql = "
        SELECT cl.clientCode,
               co.username AS coachUsername,
               co.coachID,
               lc.lastCompletedAt,
               lc.lastQuestionnaireID,
               qn.name AS lastQuestionnaireName,
               la.lastActivityAt,
               n.note AS followupNote
        FROM client cl
        INNER JOIN (
            SELECT cq.clientCode, cq.questionnaireID AS lastQuestionnaireID, cq.completedAt AS lastCompletedAt
            FROM completed_questionnaire cq
            WHERE cq.completedAt IS NOT NULL
              AND cq.rowid = (
                  SELECT c2.rowid FROM completed_questionnaire c2
                  WHERE c2.clientCode = cq.clientCode AND c2.completedAt IS NOT NULL
                  ORDER BY c2.completedAt DESC, c2.questionnaireID DESC
                  LIMIT 1
              )
        ) lc ON lc.clientCode = cl.clientCode
        LEFT JOIN questionnaire qn ON qn.questionnaireID = lc.lastQuestionnaireID
        LEFT JOIN coach co ON co.coachID = cl.coachID
        LEFT JOIN (
            SELECT qs.clientCode, MAX(qs.submittedAt) AS lastActivityAt
            FROM questionnaire_submission qs
            GROUP BY qs.clientCode
        ) la ON la.clientCode = cl.clientCode
        LEFT JOIN client_followup_note n ON n.clientCode = cl.clientCode
        WHERE $rbacClause
        ORDER BY COALESCE(la.lastActivityAt, 0) ASC, cl.clientCode ASC
    ";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($rbacParams);
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $out = [];
    foreach ($rows as $r) {
        $completedTs = $r['lastCompletedAt'] ? (int)$r['lastCompletedAt'] : null;
        $uploadTs    = $r['lastActivityAt'] !== null ? (int)$r['lastActivityAt'] : null;
        $lastAt      = null;
        if ($completedTs !== null && $uploadTs !== null) {
            $lastAt = max($completedTs, $uploadTs);
        } elseif ($completedTs !== null) {
            $lastAt = $completedTs;
        } else {
            $lastAt = $uploadTs;
        }
        $daysSince = $lastAt !== null
            ? (int)floor(($now - $lastAt) / 86400)
            : null;
        $out[] = [
            'clientCode'            => $r['clientCode'],
            'coachUsername'         => $r['coachUsername'] ?? $r['coachID'],
            'lastQuestionnaireID'   => $r['lastQuestionnaireID'],
            'lastQuestionnaireName' => $r['lastQuestionnaireName'] ?? $r['lastQuestionnaireID'],
            'lastCompletedAt'       => $completedTs ? date('Y-m-d H:i', $completedTs) : '',
            'daysSinceLastActivity' => $daysSince,
            'note'                  => $r['followupNote'] ?? '',
        ];
    }
    return $out;
}

/** Max length for counselor/admin client follow-up notes (characters). */
const QDB_CLIENT_NOTE_MAX_LENGTH = 200;

/**
 * Sanitize a client follow-up note. Empty string clears the note.
 * Rejects input that sanitizes to empty while the raw value was non-empty.
 */
function qdb_normalize_client_followup_note(mixed $note): string {
    require_once __DIR__ . '/text_sanitize.php';
    $raw = is_string($note) || is_numeric($note) ? trim((string)$note) : '';
    if ($raw === '') {
        return '';
    }
    if (mb_strlen($raw) > QDB_CLIENT_NOTE_MAX_LENGTH) {
        json_error(
            'INVALID_FIELD',
            'note must be at most ' . QDB_CLIENT_NOTE_MAX_LENGTH . ' characters',
            400
        );
    }
    $clean = sanitize_free_text($raw, QDB_CLIENT_NOTE_MAX_LENGTH);
    if ($clean === '') {
        json_error('INVALID_FIELD', 'note contains disallowed content', 400);
    }
    return $clean;
}

/**
 * Upsert a follow-up note for a client visible under the caller's RBAC.
 * Pass an already-normalized note (see qdb_normalize_client_followup_note).
 */
function qdb_analytics_set_followup_note(PDO $pdo, array $tokenRec, string $clientCode, string $note): void {
    $clientCode = trim($clientCode);
    if ($clientCode === '') {
        json_error('INVALID_FIELD', 'clientCode is required', 400);
    }

    [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl');
    $stmt = $pdo->prepare("SELECT 1 FROM client cl WHERE cl.clientCode = :cc AND ($rbacClause)");
    $stmt->execute(array_merge([':cc' => $clientCode], $rbacParams));
    if (!$stmt->fetchColumn()) {
        json_error('NOT_FOUND', 'Client not found or not authorized', 404);
    }

    $pdo->prepare(
        'INSERT INTO client_followup_note (clientCode, note, updatedByUserID, updatedAt)
         VALUES (:cc, :n, :uid, :ts) '
        . qdb_upsert_update(
            ['clientCode'],
            [
                'note' => 'excluded.note',
                'updatedByUserID' => 'excluded.updatedByUserID',
                'updatedAt' => 'excluded.updatedAt',
            ]
        )
    )->execute([
        ':cc'  => $clientCode,
        ':n'   => $note,
        ':uid' => $tokenRec['userID'] ?? '',
        ':ts'  => time(),
    ]);
}

function qdb_coach_activity_list(PDO $pdo, array $tokenRec): array {
    $role = $tokenRec['role'] ?? '';
    $entityID = $tokenRec['entityID'] ?? '';

    if ($role === 'supervisor') {
        $coachWhere = 'co.supervisorID = :eid';
        $coachParams = [':eid' => $entityID];
    } else {
        $coachWhere = '1=1';
        $coachParams = [];
    }

    $now = time();
    $d7 = $now - 7 * 86400;
    $d30 = $now - 30 * 86400;

    $sql = "
        SELECT co.coachID, co.username,
               sv.username AS supervisorUsername,
               (SELECT COUNT(*) FROM client c
                WHERE c.coachID = co.coachID AND COALESCE(c.archived, 0) = 0) AS clientCount,
               (SELECT COUNT(*) FROM questionnaire_submission qs
                INNER JOIN client c ON c.clientCode = qs.clientCode
                WHERE c.coachID = co.coachID) AS totalSubmissions,
               (SELECT COUNT(*) FROM questionnaire_submission qs
                INNER JOIN client c ON c.clientCode = qs.clientCode
                WHERE c.coachID = co.coachID AND qs.submittedAt >= :d7) AS submissions7d,
               (SELECT COUNT(*) FROM questionnaire_submission qs
                INNER JOIN client c ON c.clientCode = qs.clientCode
                WHERE c.coachID = co.coachID AND qs.submittedAt >= :d30) AS submissions30d,
               (SELECT MAX(qs.submittedAt) FROM questionnaire_submission qs
                INNER JOIN client c ON c.clientCode = qs.clientCode
                WHERE c.coachID = co.coachID) AS lastSubmissionAt
        FROM coach co
        LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID
        WHERE $coachWhere
        ORDER BY co.username
    ";
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array_merge([':d7' => $d7, ':d30' => $d30], $coachParams));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $out = [];
    foreach ($rows as $r) {
        $last = $r['lastSubmissionAt'] !== null ? (int)$r['lastSubmissionAt'] : null;
        $out[] = [
            'coachID'           => $r['coachID'],
            'username'          => $r['username'],
            'supervisorUsername'=> $r['supervisorUsername'] ?? '',
            'clientCount'       => (int)$r['clientCount'],
            'totalSubmissions'  => (int)$r['totalSubmissions'],
            'submissionsLast7d' => (int)$r['submissions7d'],
            'submissionsLast30d'=> (int)$r['submissions30d'],
            'lastSubmissionAt'  => $last ? date('Y-m-d H:i', $last) : '',
        ];
    }
    return $out;
}

function qdb_coach_recent_submissions(
    PDO $pdo,
    array $tokenRec,
    string $coachID,
    int $limit = 100,
    int $offset = 0
): array {
    $coachID = trim($coachID);
    if ($coachID === '') {
        json_error('INVALID_FIELD', 'coachID is required', 400);
    }

    $role = $tokenRec['role'] ?? '';
    $entityID = $tokenRec['entityID'] ?? '';

    $chk = $pdo->prepare('SELECT coachID, supervisorID FROM coach WHERE coachID = :id');
    $chk->execute([':id' => $coachID]);
    $coach = $chk->fetch(PDO::FETCH_ASSOC);
    if (!$coach) {
        json_error('NOT_FOUND', 'Counselor not found', 404);
    }
    if ($role === 'supervisor' && ($coach['supervisorID'] ?? '') !== $entityID) {
        json_error('FORBIDDEN', 'Not authorized for this coach', 403);
    }

    [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl');
    $limit = max(1, min($limit, 200));
    $offset = max(0, $offset);

    $countSql = "
        SELECT COUNT(*)
        FROM questionnaire_submission qs
        INNER JOIN client cl ON cl.clientCode = qs.clientCode
        WHERE cl.coachID = :cid AND ($rbacClause)
    ";
    $countStmt = $pdo->prepare($countSql);
    $countStmt->execute(array_merge([':cid' => $coachID], $rbacParams));
    $total = (int)$countStmt->fetchColumn();

    $sql = "
        SELECT qs.submissionID, qs.version, qs.submittedAt, qs.submittedByRole,
               qs.clientCode, qs.questionnaireID, qn.name AS questionnaireName
        FROM questionnaire_submission qs
        INNER JOIN client cl ON cl.clientCode = qs.clientCode
        LEFT JOIN questionnaire qn ON qn.questionnaireID = qs.questionnaireID
        WHERE cl.coachID = :cid AND ($rbacClause)
        ORDER BY qs.submittedAt DESC
        LIMIT $limit OFFSET $offset
    ";
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array_merge([':cid' => $coachID], $rbacParams));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $submissions = array_map(static function ($r) {
        return [
            'submissionID'     => $r['submissionID'],
            'version'          => (int)$r['version'],
            'submittedAt'      => $r['submittedAt'] ? date('Y-m-d H:i', (int)$r['submittedAt']) : '',
            'submittedByRole'  => $r['submittedByRole'] ?? '',
            'clientCode'       => $r['clientCode'],
            'questionnaireID'  => $r['questionnaireID'],
            'questionnaireName'=> $r['questionnaireName'] ?? $r['questionnaireID'],
        ];
    }, $rows);

    return [
        'submissions' => $submissions,
        'total'       => $total,
        'limit'       => $limit,
        'offset'      => $offset,
    ];
}
