<?php
/*
 * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * See the LICENSE file in the project root for the full license text.
 */

declare(strict_types=1);

namespace App\Auth;

use Jumbojett\OpenIDConnectClient;
use RuntimeException;

/**
 * Thin factory around jumbojett/openid-connect-php configured for
 * Microsoft Entra ID (v2.0 endpoint) with Authorization Code + PKCE.
 *
 * Reads these env vars:
 *   ENTRA_TENANT_ID
 *   ENTRA_CLIENT_ID
 *   ENTRA_CLIENT_SECRET
 *   APP_BASE_URL
 */
final class OidcClient
{
    public static function create(): OpenIDConnectClient
    {
        $tenant       = self::env('ENTRA_TENANT_ID');
        $clientId     = self::env('ENTRA_CLIENT_ID');
        $clientSecret = self::env('ENTRA_CLIENT_SECRET');
        $baseUrl      = rtrim(self::env('APP_BASE_URL'), '/');

        $issuer = "https://login.microsoftonline.com/{$tenant}/v2.0";

        $oidc = new OpenIDConnectClient($issuer, $clientId, $clientSecret);
        $oidc->setRedirectURL($baseUrl . '/auth/callback');
        $oidc->addScope(['openid', 'profile', 'email']);
        $oidc->setCodeChallengeMethod('S256'); // PKCE

        // Entra's userinfo endpoint doesn't return new info beyond the id_token
        // for our scopes; rely on verified claims to avoid the extra round trip.
        return $oidc;
    }

    public static function isConfigured(): bool
    {
        if (self::isExplicitlyDisabled()) {
            return false;
        }
        foreach (['ENTRA_TENANT_ID', 'ENTRA_CLIENT_ID', 'ENTRA_CLIENT_SECRET', 'APP_BASE_URL'] as $k) {
            $v = getenv($k);
            if (!is_string($v) || $v === '') {
                return false;
            }
        }
        return true;
    }

    /**
     * `OIDC_ENABLED=false` (or `0` / `no` / `off`) turns OIDC off even when
     * `ENTRA_*` are populated — for dev/testing on top of `LOCAL_ADMIN_*`,
     * or for an on-prem deployment that wants to keep the Entra creds in
     * .env but route everyone through the local admin fallback. Any other
     * value (including blank/unset) leaves OIDC enabled.
     */
    public static function isExplicitlyDisabled(): bool
    {
        $v = getenv('OIDC_ENABLED');
        if (!is_string($v)) {
            return false;
        }
        return in_array(strtolower(trim($v)), ['false', '0', 'no', 'off'], true);
    }

    private static function env(string $name): string
    {
        $v = getenv($name);
        if (!is_string($v) || $v === '') {
            throw new RuntimeException("Required env var not set: {$name}");
        }
        return $v;
    }
}
