<?php

/**
 * This program is copyrighted by Franz Geffke (Softmax Co., Ltd.)
 */

namespace ones\oidc;
use Exception;

include_once "ciba.php";
include_once "qr.php";
include_once "device_jwt.php";
include_once "device_token.php";
include_once "consent.php";


function check_ciba_status_loop($providerMetadata, $providerJwks, $authRequestId, $clientId, $privateKey) {
    while (true) {
        // Make device JWT
        $deviceJWT = make_device_jwt($clientId, $providerMetadata, $privateKey);
        
        try {
            $response = check_ciba_status($providerMetadata, $providerJwks, $authRequestId, $deviceJWT, $clientId);
            if ($response['status_code'] === 200) {
                // Success
                return $response['body'];
            }
        } catch (Exception $e) {
            // Ignore "Authorization pending" error and continue the loop
            if (strpos($e->getMessage(), 'Authorization pending') === false) {
                // Handle other errors
                throw new Exception("Error checking CIBA status: " . $e->getMessage());
            }
        }
        // Wait for a short period before retrying
        sleep(3);
    }
}

/**
 * CIBA script for plugin integration
 */

/**
 * User CIBA authentication flow
 * 
 * @return array(
 *   'access_token' => string,
 *   'access_token_content' => array,
 *   'id_token' => string,
 *   'id_token_content' => array
 *   'refresh_token' => string,
 *   'expires_in' => int,
 *   'scope' => string
 * )
 */
function user_ciba_auth(
    string $loginHint,
    string $clientId,
    string $privateKey,
    array $providerMetadata,
    array $providerJwks,
    string $resourceUri,
    string $bindingMessage = 'Please authorize this request',
    string $scope = 'openid profile'
) {
    try {
        // Make device JWT
        $deviceJWT = null;
        try {
            $deviceJWT = make_device_jwt($clientId, $providerMetadata, $privateKey);
        } catch (Exception $e) {
            throw new Exception("Error making device JWT: " . $e->getMessage());
        }
    
        // Make CIBA request
        $loginHintKind = 'login_hint';
        $resource = $resourceUri;
        $cibaResponse = make_ciba_request(
            $clientId,
            $providerMetadata,
            $privateKey,
            $loginHint,
            $loginHintKind,
            $scope,
            $bindingMessage,
            $deviceJWT,
            $resource);
    
        // Check CIBA status
        return check_ciba_status_loop(
            $providerMetadata,
            $providerJwks,
            $cibaResponse['auth_req_id'],
            $clientId,
            $privateKey);
    
    } catch (Exception $e) {
        echo "Error: " . $e->getMessage() . "\n";
        
        return [
            'error' => $e->getMessage()
        ];
    }
}

function user_consent_flow(
    string $userId,
    string $clientId,
    string $privateKey,
    array $providerMetadata,
    string $resourceUri,
    string $host,
    array $consentSettings,
    string $consentReason
) {
    try {
        // Make device JWT
        $deviceJWT = null;
        try {
            $deviceJWT = make_device_jwt($clientId, $providerMetadata, $privateKey);
        } catch (Exception $e) {
            throw new Exception("Error making device JWT: " . $e->getMessage());
        }
        
        // Make device access token
        $deviceAccessToken = null;
        try {
            $deviceAccessToken = request_device_access_token($providerMetadata, $clientId, $deviceJWT);
        } catch (Exception $e) {
            throw new Exception("Error making device access token: " . $e->getMessage());
        }

        // Make consent request
        $consentRequest = make_consent_request(
            $userId,
            $resourceUri,
            $host,
            $deviceAccessToken['access_token'],
            $consentSettings,
            $consentReason
        );

        while (true) {
            $deviceJWT = make_device_jwt($clientId, $providerMetadata, $privateKey);
            $deviceAccessToken = request_device_access_token($providerMetadata, $clientId, $deviceJWT);

            $consentStatus = make_consent_status_request(
                $consentRequest['id'],
                $host,
                $deviceAccessToken['access_token']
            );

            if ($consentStatus['status'] === 'approved') {
                return $consentStatus;
            } else if ($consentStatus['status'] === 'pending') {
                sleep(3);
            } else if ($consentStatus['status'] === 'denied') {
                throw new Exception("Consent request denied: " . $consentStatus['status']);
            } else {
                throw new Exception("Error: Consent request: " . $consentStatus['status']);
            }
        }
    } catch (Exception $e) {
        throw new Exception("Error: " . $e->getMessage());
    }
}

function user_ciba_auth_and_consent(
    string $loginHint,
    string $clientId,
    string $privateKey,
    array $providerMetadata,
    array $providerJwks,
    string $resourceUri,
    string $host,
    array $consentSettings,
    string $consentReason
) {
    try {
        // User CIBA authentication
        $cibaResponse = user_ciba_auth($loginHint, $clientId, $privateKey, $providerMetadata, $providerJwks, $resourceUri);
        $userId = $cibaResponse['id_token_content']['sub'];

        // User consent flow
        $consentResponse = user_consent_flow($userId, $clientId, $privateKey, $providerMetadata, $resourceUri, $host, $consentSettings, $consentReason);

        return [
            'userId' => $userId,
            'accessToken' => $cibaResponse['access_token'],
            'idToken' => $cibaResponse['id_token'],
            'refreshToken' => $cibaResponse['refresh_token'],
            'consentResponse' => $consentResponse
        ];
    } catch (Exception $e) {
        echo "Error: " . $e->getMessage() . "\n";
        
        return [
            'error' => $e->getMessage()
        ];
    }
}

/**
 * Make QR auth session
 * 
 * @param string $host The host URL
 * @param string $clientId The client ID
 * @param string $privateKey The private key
 * @param array $providerMetadata The provider metadata
 * 
 * @return array(
 *  'kind' => string,
 *  'sessionId' => string,
 *  'cbUrl' => string,
 *  'expIn' => int,
 *  'exp' => int,
 *  'interval' => int,
 *  'aud' => string,
 *  'authRequestId' => string,
 *  'cibaRequestInvoked' => bool,
 * )
 */
function make_qr_auth_session(
    string $host,
    string $clientId,
    string $privateKey,
    array $providerMetadata
) {
    try {
        // Make device JWT and get access token
        $deviceJWT = make_device_jwt($clientId, $providerMetadata, $privateKey);
        $deviceAccessToken = request_device_access_token($providerMetadata, $clientId, $deviceJWT);

        // Create QR auth session
        return make_qr_auth_session_request($host, $deviceAccessToken['access_token']);
    } catch (Exception $e) {
        throw new Exception("Error in make_qr_auth_session: " . $e->getMessage());
    }
}

/**
 * Poll QR auth session and start CIBA flow if ready
 * 
 * @return array(
 *  'kind' => string,
 *  'sessionId' => string,
 *  'cbUrl' => string,
 *  'expIn' => int,
 *  'exp' => int,
 *  'interval' => int,
 *  'aud' => string,
 *  'authRequestId' => string,
 *  'cibaRequestInvoked' => bool,
 *  'loginHintToken' => string
 * )
 */
function poll_qr_auth_session(
    string $sessionId,
    string $clientId,
    string $privateKey,
    array $providerMetadata,
    string $resourceUri,
    string $host,
    string $bindingMessage = 'Please authorize this request',
    string $scope = 'openid profile'
) {
    try {
        // Make device JWT and token
        $deviceJWT = make_device_jwt($clientId, $providerMetadata, $privateKey);
        $deviceAccessToken = request_device_access_token($providerMetadata, $clientId, $deviceJWT);
        
        // Check QR session status
        $status = verify_qr_auth_session($host, $sessionId, $deviceAccessToken['access_token']);
        
        if (!$status) {
            return null;
        }

        // If we have login hint token and no CIBA request yet, start CIBA flow
        if (isset($status['loginHintToken']) && !isset($status['authRequestId'])) {
            // Make CIBA request
            $loginHint = $status['loginHintToken'];
            $loginHintKind = 'login_hint_token';
            // $scope = 'openid profile';
            // $bindingMessage = 'Please authorize this request';

            $deviceJWT = make_device_jwt($clientId, $providerMetadata, $privateKey);
            
            make_ciba_request(
                $clientId,
                $providerMetadata,
                $privateKey,
                $loginHint,
                $loginHintKind,
                $scope,
                $bindingMessage,
                $deviceJWT,
                $resourceUri,
                $status['sessionId']
            );
        }
        
        return $status;
    } catch (Exception $e) {
        if (strpos($e->getMessage(), 'Authorization pending') !== false) {
            return null;
        }
        throw new Exception("Error polling QR session: " . $e->getMessage());
    }
}

?>