<?php
/**
 * Access control for RBC AI agent endpoints.
 *
 * ai_dashboard.php gates the *page* on role and on requireModule('ai_agent'),
 * but the endpoints it posts to checked only that someone was logged in. Any
 * authenticated user — a tutor, a registrar — could POST directly and trigger
 * bulk SMS, mass parent notifications, or bulk parent-account creation (which
 * generates passwords and sends them out), bypassing both the role restriction
 * and the paid-module licence check.
 *
 * Every AI endpoint must call rbcRequireAgentAccess() before doing any work.
 */

if (!defined('DB_HOST')) {
    require_once __DIR__ . '/../config.php';
}

/** Roles allowed to drive the agent — mirrors ai_dashboard.php. */
if (!defined('RBC_AGENT_ROLES')) {
    define('RBC_AGENT_ROLES', ['admin', 'accountant', 'director', 'supervisor', 'headteacher']);
}

if (!function_exists('rbcAgentCsrfToken')) {
    /** Current session CSRF token, minted on first use. */
    function rbcAgentCsrfToken(): string
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
        if (empty($_SESSION['rbc_csrf_token'])) {
            $_SESSION['rbc_csrf_token'] = bin2hex(random_bytes(32));
        }
        return (string) $_SESSION['rbc_csrf_token'];
    }
}

if (!function_exists('rbcAgentCsrfValid')) {
    /** Constant-time comparison of a supplied token against the session token. */
    function rbcAgentCsrfValid(?string $supplied): bool
    {
        $expected = (string) ($_SESSION['rbc_csrf_token'] ?? '');
        if ($expected === '' || $supplied === null || $supplied === '') {
            return false;
        }
        return hash_equals($expected, $supplied);
    }
}

if (!function_exists('rbcAgentDeny')) {
    /**
     * Emit a denial in the caller's wire format and stop.
     *
     * @param string $format 'json' or 'sse'
     */
    function rbcAgentDeny(int $status, string $message, string $format = 'json'): void
    {
        http_response_code($status);
        if ($format === 'sse') {
            header('Content-Type: text/event-stream');
            header('Cache-Control: no-cache');
            echo "event: error\n";
            echo 'data: ' . json_encode(['message' => $message]) . "\n\n";
            echo "event: done\n";
            echo 'data: ' . json_encode(['thread_id' => '']) . "\n\n";
        } else {
            header('Content-Type: application/json');
            echo json_encode(['success' => false, 'error' => $message]);
        }
        exit;
    }
}

if (!function_exists('rbcRequireAgentAccess')) {
    /**
     * Enforce login, role, module licence and (optionally) CSRF.
     * Returns the authenticated user id.
     *
     * @param string      $format     'json' | 'sse' — how to render a denial.
     * @param bool        $check_csrf Set false only for endpoints that cannot
     *                                carry a token (there are none today).
     * @param string|null $token      Token supplied by the caller.
     */
    function rbcRequireAgentAccess(
        string  $format = 'json',
        bool    $check_csrf = true,
        ?string $token = null
    ): int {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        if (empty($_SESSION['user_id'])) {
            rbcAgentDeny(401, 'Not signed in.', $format);
        }

        $role = (string) ($_SESSION['role'] ?? '');
        if (!in_array($role, RBC_AGENT_ROLES, true)) {
            error_log("RBC agent: role '{$role}' (user {$_SESSION['user_id']}) denied.");
            rbcAgentDeny(403, 'Your role does not have access to the AI agent.', $format);
        }

        // Licence / module entitlement. requireModule() emits its own JSON 403
        // for ajax callers; for SSE we pre-empt it so the client sees an event.
        if (function_exists('requireModule')) {
            if ($format === 'sse') {
                if (!defined('EDUTRACK_MODULES')) {
                    require_once __DIR__ . '/../module_definitions.php';
                }
                $access = defined('ROLE_ACCESS') ? (ROLE_ACCESS[$role] ?? []) : 'all';
                if ($access !== 'all' && !in_array('ai_agent', (array) $access, true)) {
                    rbcAgentDeny(403, 'The AI agent module is not enabled for your role.', 'sse');
                }
            }
            requireModule('ai_agent', true);
        }

        if ($check_csrf && !rbcAgentCsrfValid($token)) {
            error_log("RBC agent: CSRF check failed for user {$_SESSION['user_id']}.");
            rbcAgentDeny(403, 'Security token missing or invalid. Please reload the page.', $format);
        }

        return (int) $_SESSION['user_id'];
    }
}

if (!function_exists('rbcActionNeedsConfirmation')) {
    /**
     * Actions that spend money or push data to third parties. The system prompt
     * asks the model to confirm these, but nothing enforced it — so a single
     * ambiguous sentence could fire bulk SMS to every parent in the school.
     * The client must echo confirm=true for these to run.
     *
     * @return string|null Human-readable warning, or null when no confirmation is needed.
     */
    function rbcActionNeedsConfirmation(string $action, array $params): ?string
    {
        switch ($action) {
            case 'send_fee_reminders':
                $scope = trim((string) ($params['class_name'] ?? '')) ?: 'ALL classes';
                return "This sends fee reminder SMS to parents in {$scope}. SMS costs money and cannot be recalled.";

            case 'bulk_notify_parents':
                $target = (string) ($params['target'] ?? 'all');
                $scope  = $target === 'class'
                    ? (trim((string) ($params['class_name'] ?? '')) ?: 'a class')
                    : 'EVERY parent in the school';
                $sms = !empty($params['send_sms']) ? ' and SMS' : '';
                return "This sends a notification{$sms} to {$scope}.";

            case 'create_parent_accounts_bulk':
                $scope = trim((string) ($params['class_name'] ?? '')) ?: 'ALL classes';
                return "This creates parent portal accounts for {$scope} and SMS's login passwords to them.";

            case 'send_weekly_feeding_summary':
                return 'This sends the weekly feeding summary SMS to every parent with canteen activity.';

            case 'send_canteen_reminder_sms':
                return 'This sends canteen reminder SMS to parents of students with pending entitlements.';

            case 'cancel_canteen_session':
                return 'This cancels the canteen session and SMS-notifies affected parents.';

            default:
                return null;
        }
    }
}
