<?php
// Prevent direct access
if (count(get_included_files()) === 1) {
    http_response_code(403);
    exit('Forbidden');
}

/**
 * RoleMiddleware — permisos por rol y scope por tenant.
 *
 * Roles:
 *   super_admin — sesión en admins table ($_SESSION['admin_id'])
 *   seller      — sesión en users table, role='seller', ve su pipeline
 *   owner       — sesión en users table, role='owner', solo su tenant
 *   staff       — sesión en users table, role='staff', solo agenda
 *   support     — sesión en users table, role='support', tenants asignados
 *
 * Uso:
 *   RoleMiddleware::requireAuth();
 *   RoleMiddleware::requireRole(['owner','super_admin']);
 *   RoleMiddleware::requireTenantAccess($db, $tenantId);
 *   $ctx = RoleMiddleware::context();  // datos del usuario autenticado
 */
class RoleMiddleware {

    /**
     * Garantiza que hay una sesión activa (cualquier rol o super_admin).
     * Redirige a /login si no hay sesión.
     */
    public static function requireAuth(bool $apiMode = false): void {
        if (isset($_SESSION['admin_id'])) return; // super_admin siempre pasa
        if (isset($_SESSION['user_id']))  return;

        if ($apiMode) {
            http_response_code(401);
            echo json_encode(['error' => 'no_auth']);
            exit;
        }
        header('Location: /login');
        exit;
    }

    /**
     * Garantiza que el usuario autenticado tiene al menos uno de los roles dados.
     * $roles puede incluir 'super_admin', 'owner', 'staff', 'seller', 'support'.
     */
    public static function requireRole(array $roles, bool $apiMode = false): void {
        self::requireAuth($apiMode);
        $ctx = self::context();

        if (in_array($ctx['role'], $roles, true)) return;

        if ($apiMode) {
            http_response_code(403);
            echo json_encode(['error' => 'forbidden', 'required' => $roles]);
            exit;
        }
        http_response_code(403);
        echo '403 Forbidden';
        exit;
    }

    /**
     * Verifica que el usuario autenticado puede acceder al tenant dado.
     *
     * Reglas de scope:
     *   super_admin → acceso a cualquier tenant
     *   owner/staff/support → solo su propio tenant_id
     *   seller → solo tenants donde tenants.seller_id = sellers.id del usuario
     */
    public static function requireTenantAccess(PDO $db, int $tenantId, bool $apiMode = false): void {
        self::requireAuth($apiMode);
        $ctx = self::context();

        if ($ctx['role'] === 'super_admin') return;

        if (in_array($ctx['role'], ['owner', 'staff', 'support'], true)) {
            if ((int)($ctx['tenant_id'] ?? 0) === $tenantId) return;
            self::deny($apiMode);
        }

        if ($ctx['role'] === 'seller') {
            $stmt = $db->prepare(
                "SELECT 1 FROM tenants WHERE id = :tid AND seller_id = :sid AND deleted_at IS NULL LIMIT 1"
            );
            $stmt->execute([':tid' => $tenantId, ':sid' => $ctx['seller_profile_id']]);
            if ($stmt->fetchColumn()) return;
            self::deny($apiMode);
        }

        self::deny($apiMode);
    }

    /**
     * Retorna el contexto del usuario autenticado.
     * Siempre incluye: role, display_name.
     * Para roles tenant-scoped incluye: user_id, tenant_id.
     * Para seller incluye: seller_profile_id.
     */
    public static function context(): array {
        // super_admin via admins table
        if (isset($_SESSION['admin_id'])) {
            return [
                'role'         => 'super_admin',
                'user_id'      => null,
                'tenant_id'    => null,
                'display_name' => $_SESSION['admin_name'] ?? 'Admin',
                'seller_profile_id' => null,
            ];
        }

        // Usuario normal (owner/staff/seller/support)
        if (isset($_SESSION['user_id'])) {
            return [
                'role'              => $_SESSION['role']              ?? 'owner',
                'user_id'           => (int)$_SESSION['user_id'],
                'tenant_id'         => isset($_SESSION['tenant_id']) ? (int)$_SESSION['tenant_id'] : null,
                'display_name'      => $_SESSION['user_name']         ?? '',
                'seller_profile_id' => isset($_SESSION['seller_profile_id'])
                                         ? (int)$_SESSION['seller_profile_id']
                                         : null,
            ];
        }

        return ['role' => 'guest', 'user_id' => null, 'tenant_id' => null, 'display_name' => '', 'seller_profile_id' => null];
    }

    /**
     * Scope de tenants visible para el usuario autenticado.
     * Retorna un fragmento SQL WHERE para filtrar queries cross-tenant.
     *
     * Ejemplo:
     *   [$where, $params] = RoleMiddleware::tenantScope($db);
     *   $stmt = $db->prepare("SELECT * FROM tenants WHERE $where");
     *   $stmt->execute($params);
     */
    public static function tenantScope(PDO $db): array {
        $ctx = self::context();

        if ($ctx['role'] === 'super_admin') {
            return ['1=1', []];
        }

        if ($ctx['role'] === 'seller') {
            $sid = (int)$ctx['seller_profile_id'];
            return ['tenants.seller_id = :_scope_seller_id', [':_scope_seller_id' => $sid]];
        }

        // owner / staff / support — solo su tenant
        $tid = (int)$ctx['tenant_id'];
        return ['tenants.id = :_scope_tenant_id', [':_scope_tenant_id' => $tid]];
    }

    /**
     * Carga y cachea la sesión de usuario desde la DB.
     * Llamar en login tras verificar credenciales.
     */
    public static function startSession(array $user): void {
        session_regenerate_id(true);
        $_SESSION = [];
        $_SESSION['user_id']   = $user['id'];
        $_SESSION['tenant_id'] = $user['tenant_id'];
        $_SESSION['role']      = $user['role'];
        $_SESSION['user_name'] = $user['name'] ?? '';

        if (!empty($user['seller_profile_id'])) {
            $_SESSION['seller_profile_id'] = $user['seller_profile_id'];
        }
    }

    // ── Helpers privados ──────────────────────────────────────────────────────

    private static function deny(bool $apiMode): never {
        if ($apiMode) {
            http_response_code(403);
            echo json_encode(['error' => 'forbidden']);
            exit;
        }
        http_response_code(403);
        echo '403 Forbidden';
        exit;
    }
}
