<?php
namespace App\Security;

/**
 * Declarative RBAC classification for every dispatched API action.
 *
 * The front controller (public/index.php) enforces capability-gated actions via its
 * $rbacActionCapabilities map. The remaining actions are classified here so that
 * EVERY endpoint has an explicit, reviewable RBAC posture and nothing is silently
 * unguarded. scripts/rbac/verify_routes.php fails CI if a dispatched action is not
 * in the capability map nor in one of these buckets — so a new feature cannot ship
 * without declaring how RBAC applies to it.
 *
 * Buckets:
 *  - PUBLIC: reachable before authentication (setup/login only).
 *  - AUTH_ONLY: requires a logged-in user but intentionally no capability (e.g. a
 *    user reading their own permissions/notifications, or form-config needed to
 *    render any screen). Documented exceptions, reviewed deliberately.
 *  - CONTROLLER_GUARDED: not in the central map because the controller enforces the
 *    capability (and usually data scope) itself via requireRbac()/isAdmin().
 */
class RouteCapabilities
{
    /** Pre-auth endpoints. Keep this list tiny and obvious. */
    public const PUBLIC_ACTIONS = [
        'setup_init',
        'setup_complete',
        'setup_sync',
        'get_sync_status',
        'resume_sync',
        'get_login_users',
        'login',
        'logout',
    ];

    /** Authenticated, but deliberately not capability-gated. */
    public const AUTH_ONLY_ACTIONS = [
        'rbac_current_permissions',          // a user reading their own resolved permissions
        'log_client_event',                  // client telemetry (namespaced server-side)
        'get_lookups',                       // dropdown data needed across screens
        'get_field_configurations_public',   // field config needed to render forms for any role
        'batch_update_virtual_stages',       // maintenance; auth-gated (see index.php)
        'generate_deal_title',               // helper used while creating a deal
        'get_allowed_services',              // service catalog for deal forms
    ];

    /** Capability (and usually scope) enforced inside the controller, not the central map. */
    public const CONTROLLER_GUARDED_ACTIONS = [
        // LeadController
        'get_leads', 'lookup_mobile', 'get_lead_didar_data', 'get_person_profile',
        'create_lead', 'update_lead', 'delete_lead', 'transfer_owner', 'get_referrals',
        'backfill_lead_customer_products_from_requested_services',
        // DealController
        'get_deals', 'get_legacy_deals', 'save_deal', 'set_deal_status', 'set_deal_pipeline_stage',
        'set_course_status', 'delete_deal', 'deal/reactivate', 'deal/force_success_exit_kariz',
        'deal/move_to_parked', 'deal/move_out_of_parked', 'deal/move_stage_to_parked',
        'deal/move_stage_out_of_parked', 'deal/unpark_all',
        'deal/reconcile_won_kariz_preview', 'deal/reconcile_won_kariz_run',
        'deal/reconcile_lost_kariz_preview', 'deal/reconcile_lost_kariz_run',
        'upload_payment_file', 'payment_file', 'get_deal',
        // KpiController
        'get_kpi', 'get_kpi_section', 'set_kpi_target', 'get_kpi_targets', 'get_kpi_deals',
        'get_kpi_drilldown', 'get_performance_dashboard', 'get_performance_dashboard_section',
        // ExportController
        'export_catalog', 'export_run',
        // IntegrationController
        'get_lead_assignment', 'get_lead_vip_access', 'reconcile_app_lead',
        // CommissionController / payroll compatibility endpoints
        'get_commission_matrix_data', 'get_commission_setup_data', 'save_commission_rule',
        'store_commission_rule', 'get_commission_rules', 'assign_commission_rules',
        'toggle_commission_rule', 'toggle_commission_assignment', 'update_commission_assignment', 'delete_commission_assignment', 'get_deal_commissions', 'calculate_commission_ledger', 'preview_commission_ledger', 'get_deal_commission_by_deal', 'update_deal_commission_status',
        'delete_deal_commission', 'bulk_delete_commission_rules', 'delete_commission_rule',
        'bulk_delete_deal_commissions', 'get_commission_kpis',
        'aggregate_commission_payout_cycles', 'get_commission_payout_queue', 'get_commission_drilldown', 'decide_commission_payout',
        // Financial payment/cheque controllers
        'get_cheques', 'set_cheque_status', 'set_cheque_collection',
        'get_financial_payments', 'mark_payment_sepidar',
        'get_finance_cheque_inbox', 'finance_approve_cheque', 'finance_reject_cheque',
        // ServiceGovernanceController
        'get_service_governance_settings', 'save_service_governance_settings',
        // ImportController (admin)
        'bulk_phone_lookup', 'import_leads_preview', 'import_leads_execute', 'download_leads_import_template',
        // RbacController (admin)
        'rbac_catalog', 'rbac_role_permissions', 'rbac_user_permissions', 'rbac_save_role',
        'rbac_clone_role', 'rbac_delete_role', 'rbac_save_role_capabilities', 'rbac_save_role_scopes',
        'rbac_save_user_capabilities', 'rbac_save_user_scopes',
        // NotificationController (admin-only test endpoint)
        'test_notification',
    ];

    public static function bucketFor(string $action): ?string
    {
        if (in_array($action, self::PUBLIC_ACTIONS, true)) return 'public';
        if (in_array($action, self::AUTH_ONLY_ACTIONS, true)) return 'auth_only';
        if (in_array($action, self::CONTROLLER_GUARDED_ACTIONS, true)) return 'controller_guarded';
        return null;
    }

    public static function all(): array
    {
        return [
            'public' => self::PUBLIC_ACTIONS,
            'auth_only' => self::AUTH_ONLY_ACTIONS,
            'controller_guarded' => self::CONTROLLER_GUARDED_ACTIONS,
        ];
    }
}
