<?php
// /lib/auth.php
declare(strict_types=1);

/**
 * Auth helpers for admin + multi-tenant roles.
 *
 * Usage patterns:
 *
 *   require_once __DIR__ . '/auth.php';
 *
 *   // Generic guard (any signed-in user)
 *   auth_require();
 *
 *   // Require specific roles
 *   auth_require(['role' => 'admin']);
 *   auth_require(['role' => ['admin', 'company_admin']]);
 *
 *   // Admin area (project convention)
 *   require_admin_auth($config);                    // admin + company_admin
 *   require_admin_auth($config, ['admin']);         // admin only
 *
 *   // Role helpers
 *   is_admin_user();
 *   is_company_admin_user();
 *
 *   // Tenancy guard for rows
 *   can_access_company_scope($orgId, $companyId);
 */

if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}

require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/db.php'; // expects $pdo

/** Return current signed-in user array or null. */
function auth_user(): ?array
{
    return $_SESSION['user'] ?? null;
}

/** Is someone signed in with a user id? */
function auth_is_signed_in(): bool
{
    return !empty($_SESSION['user']) && !empty($_SESSION['user']['id']);
}

/** Absolute current URL, used for return-to. */
function auth_current_url(): string
{
    $https  = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
           || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
    $scheme = $https ? 'https' : 'http';
    $host   = $_SERVER['HTTP_HOST'] ?? 'localhost';
    $uri    = $_SERVER['REQUEST_URI'] ?? '/';
    return $scheme . '://' . $host . $uri;
}

/**
 * Refresh session user from DB on each request (once).
 *
 * - Uses users.id from session.
 * - Syncs: email, name, role, organisation_id, company_id
 * - Keeps provider/other custom keys from session.
 * - If user row no longer exists -> logs out (no redirect here).
 */
function auth_refresh_user(): void
{
    static $done = false;
    if ($done) {
        return;
    }
    $done = true;

    if (!auth_is_signed_in()) {
        return;
    }

    $u   = $_SESSION['user'];
    $uid = (int)($u['id'] ?? 0);
    if ($uid <= 0) {
        return;
    }

    global $pdo;
    if (!$pdo instanceof PDO) {
        return;
    }

    try {
        $sql = "SELECT id, email, name, role, organisation_id, company_id
                FROM users
                WHERE id = ?";
        $stmt = $pdo->prepare($sql);
        if (!$stmt) {
            return;
        }
        $stmt->execute([$uid]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$row) {
            // User deleted/disabled → clear session silently
            $_SESSION = [];
            if (session_status() === PHP_SESSION_ACTIVE) {
                session_destroy();
            }
            return;
        }

        // Merge DB fields over existing session; keep provider etc.
        $_SESSION['user'] = array_merge($u, [
            'email'           => $row['email'] ?? ($u['email'] ?? ''),
            'name'            => $row['name']  ?? ($u['name']  ?? ''),
            'role'            => $row['role']  ?? ($u['role']  ?? 'staff'),
            'organisation_id' => isset($row['organisation_id'])
                                  ? (int)$row['organisation_id']
                                  : ($u['organisation_id'] ?? null),
            'company_id'      => isset($row['company_id'])
                                  ? (int)$row['company_id']
                                  : ($u['company_id'] ?? null),
        ]);

    } catch (Throwable $e) {
        // Fail soft: if DB borks, keep existing session.
        // Optional: log somewhere if you have a logger.
    }
}

/**
 * Generic guard (backwards compatible).
 *
 * Options:
 *   - role: 'admin' | 'staff' | 'company_admin' | array of roles
 *   - redirect: override login URL
 */
function auth_require(array $opts = []): void
{
    // Load config for base_url + login URL
    $config = require __DIR__ . '/config.php';
    $base   = rtrim($config['base_url'] ?? '', '/');
    $login  = $opts['redirect'] ?? ($base . '/admin/login.php');

    auth_refresh_user();

    if (!auth_is_signed_in()) {
        $to = $login . '?r=' . urlencode(auth_current_url());
        header('Location: ' . $to);
        exit;
    }

    if (!empty($opts['role'])) {
        $need = is_array($opts['role']) ? $opts['role'] : [$opts['role']];
        $me   = auth_user();
        $role = $me['role'] ?? null;
        if (!$role || !in_array($role, $need, true)) {
            http_response_code(403);
            echo 'Forbidden: insufficient privileges.';
            exit;
        }
    }
}

/** Logout helper. */
function auth_logout_and_redirect(string $to): void
{
    $_SESSION = [];
    if (session_status() === PHP_SESSION_ACTIVE) {
        session_destroy();
    }
    header('Location: ' . $to);
    exit;
}

/* ============================================================
 * Multi-tenant / roles helpers
 * ==========================================================*/

/** True if current user is a full admin. */
function is_admin_user(?array $u = null): bool
{
    $u = $u ?? auth_user();
    return ($u['role'] ?? null) === 'admin';
}

/** True if current user is a company_admin. */
function is_company_admin_user(?array $u = null): bool
{
    $u = $u ?? auth_user();
    return ($u['role'] ?? null) === 'company_admin';
}

/**
 * Standard guard for admin area pages.
 *
 * Usage:
 *   require_once __DIR__ . '/auth.php';
 *   require_admin_auth($config);                // admin + company_admin
 *   require_admin_auth($config, ['admin']);     // admin only
 */
function require_admin_auth(array $config, array $allowedRoles = ['admin','company_admin']): void
{
    if (session_status() !== PHP_SESSION_ACTIVE) {
        session_start();
    }

    $base  = rtrim($config['base_url'] ?? '', '/');
    $login = $base . '/admin/login.php';

    auth_refresh_user();

    if (!auth_is_signed_in()) {
        $to = $login . '?r=' . urlencode(auth_current_url());
        header('Location: ' . $to);
        exit;
    }

    if ($allowedRoles) {
        $me   = auth_user();
        $role = $me['role'] ?? null;
        if (!$role || !in_array($role, $allowedRoles, true)) {
            http_response_code(403);
            echo 'Forbidden: insufficient privileges.';
            exit;
        }
    }
}

/**
 * Row-level tenancy helper.
 *
 * Rules:
 *  - admin           → full access.
 *  - company_admin   → can access only rows with matching company_id
 *                      (and if both sides have organisation_id, that must match too).
 *  - others          → false.
 */
function can_access_company_scope(?int $organisationId, ?int $companyId): bool
{
    $u = auth_user();
    if (!$u) {
        return false;
    }

    $role = $u['role'] ?? '';

    if ($role === 'admin') {
        return true;
    }

    if ($role === 'company_admin') {
        $uOrg = isset($u['organisation_id']) ? (int)$u['organisation_id'] : null;
        $uCo  = isset($u['company_id'])      ? (int)$u['company_id']      : null;

        if ($uCo && $companyId && $uCo === (int)$companyId) {
            if ($organisationId && $uOrg && $organisationId !== $uOrg) {
                return false;
            }
            return true;
        }
    }

    return false;
}
