<?php

namespace Helpers\SSO;

use GuzzleHttp\Utils;
use Helpers\Outil\Document;
use Helpers\Outil\XmlHelper;
use Helpers\Session\ParseBase1;
use Psr\Container\ContainerInterface;

class ADFS
{
   
    private ?string $login;

    /**
     * Summary of __construct
     * @param \Psr\Container\ContainerInterface $contenaire
     * @param array<mixed> $post
     * @param mixed $metadata
     * @param mixed $logout
     * @return bool|string|null
     */
    public function __construct(ContainerInterface $contenaire, $post, \PDO $pdo, $metadata = false, $logout = false)
    {
         $x509 = \Modules\Outil\Outil\GestionFile::getFile((new ParseBase1())->getRep()->etc.'/ADFS/cert/509cert.txt');
         $privateKey = \Modules\Outil\Outil\GestionFile::getFile((new ParseBase1())->getRep()->etc.'/ADFS/cert/privateKey.txt');

        //  $parametrage = \Modules\Outil\Outil\GestionFile::getFile((new ParseBase1())->getRep()->etc.'/ADFS/Parametres.json');
        //  $parametrage = json_decode($parametrage, true);

        try {
            $urlMetadataIDP = new \Entity\Model\Paravrac($pdo);
            
            $typeChemin = $urlMetadataIDP->find(['TYPEPARV' => 'PARAMADFS', 'CODEPARV' => 'TYPECHEMINMETADATA'])['LIB01PARV'] ?? null;
            
            $urlMetadataIDP = $urlMetadataIDP->find(['TYPEPARV' => 'PARAMADFS', 'CODEPARV' => 'URLCLIENT'])['LIB01PARV'];
            /**
             *
             * Fichier metadata du client (IDP)
             */
            if ($typeChemin!='F') {
                $metadataIDP = @\Modules\Outil\Outil\GestionFile::getFile($urlMetadataIDP);
            } else {
                $metadataIDP = @\Modules\Outil\Outil\GestionFile::getFile((new ParseBase1())->getRep()->pj.$urlMetadataIDP);
            }

            $x5092_IDP = '';
            $x5092_IDP2 = '';
            if ($metadataIDP) {
                $dom = new \DOMDocument();
                $dom->preserveWhiteSpace = false;
                $dom->formatOutput = true;
                $dom->loadXML($metadataIDP);
                
                $dom = Document::removeAllNamespaces($dom);
                $metadataIDP = XmlHelper::saveXML($dom);
            
                $metadataIDP = new \SimpleXMLElement($metadataIDP);
               
                $x5092_IDP2 = $metadataIDP->IDPSSODescriptor ->KeyDescriptor->KeyInfo->X509Data->X509Certificate;
                $x5092_IDP = '';
                
                try {
                    $x5092_IDP =  $metadataIDP->Signature->KeyInfo->X509Data->X509Certificate;
                // @phpstan-ignore-next-line
                } catch (\Exception $e) {
                    $x5092_IDP = $x5092_IDP2;
                }
                if (trim($x5092_IDP)=='') {
                    $x5092_IDP = $x5092_IDP2;
                }
                
                $entity_IDP = $metadataIDP->attributes()->entityID;


                $singleSignOnService_IDP = $metadataIDP->IDPSSODescriptor->SingleSignOnService->attributes()->Location;
                $singleLogoutService = $metadataIDP->IDPSSODescriptor->SingleLogoutService->attributes()->Location ?? '';
            } else {
                $metadataIDP = '';
            
                $x5092_IDP = '';
                $entity_IDP = '';
                $singleSignOnService_IDP = '';
            }
        } catch (\Exception $e) {
        }
       
        // if(!isset($x5092) && !$metadata)
        // {
        //     if(isset($_SESSION['tokenAuthentification']))
        //     {
        //         unset($_SESSION['tokenAuthentification']);
        //         unset($_SESSION['MobileTypeConnexion']);
        //         unset($_SESSION['AlreadyConnected']);
        //     }
        //     echo "Missing setting";
        // }

        // $uri =  $contenaire->get(\Psr\Http\Message\ServerRequestInterface::class)->getUri()->getPath();
        
        // $baseUrl =(strpos($uri, $contenaire->get('baseUrl').'V2/index/metadata') === 0) ?
        //     $contenaire->get('urlOxaproc').'/V2'
        //     :
        //      $contenaire->get('urlOxaproc');
       
        $baseUrl = $contenaire->get('urlOxaproc').'/V2';
 
        $settings = array (
            'strict' => true,
            'debug' => true,
            'baseurl' => $baseUrl.'/index',
            'sp' => array (
                'entityId' => $baseUrl.'/index/metadata',
                'assertionConsumerService' => array (
                    'url' => $baseUrl.'/index/ADFS',
                    'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
                ),
         
                'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
                'x509cert' => $x509,
                'privateKey' => $privateKey,
                
            ),
            'compress' => array (
                'requests' => true,
                'responses' => true
            ),
            'idp' => array (
                // Identifier of the IdP entity  (must be a URI)
                'entityId' => trim($entity_IDP ?? ''),
                'singleSignOnService' => array (
                    // URL Target of the IdP where the SP will send the Authentication Request Message
                    'url' => trim($singleSignOnService_IDP ?? ''),
                    // SAML protocol binding to be used when returning the <Response>
                    // message.  Onelogin Toolkit supports for this endpoint the
                    // HTTP-Redirect binding only
                    'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
                ),
                'singleLogoutService' => array (
                    // URL Location of the IdP where SLO Request will be sent.
                    'url' => $singleLogoutService ?? '',
                    // URL location of the IdP where the SP will send the SLO Response (ResponseLocation)
                    // if not set, url for the SLO Request will be used
                    'responseUrl' => '',
                    // SAML protocol binding to be used when returning the <Response>
                    // message. OneLogin Toolkit supports the HTTP-Redirect binding
                    // only for this endpoint.
                    'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
                ),

                'x509cert' => $x5092_IDP ?? '',
                'x509certMulti' => array(
                    'signing' => array(
                        0 => $x5092_IDP ?? '',
                    ),
                    'encryption' => array(
                        0 => $x5092_IDP2 ?? '',
                    )
                ),

                
            ),
            
        'retrieveParametersFromServer' => false,
            'security' => array (
                //'lowercaseUrlencoding' => false,
                
                /** signatures and encryptions offered */
        
                // Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
                // will be encrypted.
        
                // Indicates whether the <samlp:AuthnRequest> messages sent by this SP
                // will be signed.  [Metadata of the SP will offer this info]
                'authnRequestsSigned' => false,
                'signMetadata' => array (
                    'x509cert' => $x509,
                    'privateKey' => $privateKey
               ),


               'logoutRequestSigned' => false,

                /** signatures and encryptions required **/
        
                // Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest> and
                // <samlp:LogoutResponse> elements received by this SP to be signed.
                'wantMessagesSigned' => false,
        
                'nameIdEncrypted' => false,
                // Indicates a requirement for the <saml:Assertion> elements received by
                // this SP to be encrypted.
                'wantAssertionsEncrypted' => false,
                
        
                // Indicates a requirement for the <saml:Assertion> elements received by
                // this SP to be signed.        [The Metadata of the SP will offer this info]
                'wantAssertionsSigned' => false,
                'lowercaseUrlencoding' => false,
                'requestedAuthnContext' => false,
               
            ),
    
        );
        if ($metadata) {
            $samlSettings = new \OneLogin\Saml2\Settings($settings, true);
            $sp = $samlSettings->getSPData();
            $samlMetadata = \OneLogin\Saml2\Metadata::builder($sp, false, false);
            $samlMetadata = \OneLogin\Saml2\Metadata::addX509KeyDescriptors($samlMetadata, $x509);
            // $samlMetadata = \OneLogin\Saml2\Metadata::signMetadata($samlMetadata, $privateKey, $x509,
            // \RobRichards\XMLSecLibs\XMLSecurityKey::RSA_SHA256,\RobRichards\XMLSecLibs\XMLSecurityKey::RSA_SHA1);
            echo  $samlMetadata;
            @exit;
        } else {
            if (!isset($post['SAMLResponse'])) {
                $settings = new \OneLogin\Saml2\Auth($settings);
                if ($logout) {
                    $settings->logout();
                } else {
                    $settings->login();
                }
            }
    
            $settings = new \OneLogin\Saml2\Settings($settings, true);
            $auth = new \OneLogin\Saml2\Response($settings, $post['SAMLResponse']);
    
            if ($auth->isValid()) {
                $this->login = $auth->getNameId();
            }
        }
    }

    public static function getMetadata(ContainerInterface $contenaire, \PDO $pdo) :mixed
    {
        $x509 = \Modules\Outil\Outil\GestionFile::getFile((new ParseBase1())->getRep()->etc.'/ADFS/cert/509cert.txt');
        $privateKey = \Modules\Outil\Outil\GestionFile::getFile((new ParseBase1())->getRep()->etc.'/ADFS/cert/privateKey.txt');

       //  $parametrage = \Modules\Outil\Outil\GestionFile::getFile((new ParseBase1())->getRep()->etc.'/ADFS/Parametres.json');
       //  $parametrage = json_decode($parametrage, true);

        try {
            $urlMetadataIDP = new \Entity\Model\Paravrac($pdo);
            
            $typeChemin = $urlMetadataIDP->find(['TYPEPARV' => 'PARAMADFS', 'CODEPARV' => 'TYPECHEMINMETADATA'])['LIB01PARV'] ?? null;
            
            $urlMetadataIDP = $urlMetadataIDP->find(['TYPEPARV' => 'PARAMADFS', 'CODEPARV' => 'URLCLIENT'])['LIB01PARV'];
            if ($typeChemin!='F') {
                $metadataIDP = @\Modules\Outil\Outil\GestionFile::getFile($urlMetadataIDP);
            } else {
                $metadataIDP = @\Modules\Outil\Outil\GestionFile::getFile((new ParseBase1())->getRep()->pj.$urlMetadataIDP);
            }

            if ($metadataIDP) {
                $dom = new \DOMDocument();
                $dom->preserveWhiteSpace = false;
                $dom->formatOutput = true;
                $dom->loadXML($metadataIDP);
                
                $dom = Document::removeAllNamespaces($dom);
                $metadataIDP = XmlHelper::saveXML($dom);
            
                $metadataIDP = new \SimpleXMLElement($metadataIDP);
        
                $x5092_IDP = $metadataIDP->IDPSSODescriptor ->KeyDescriptor->KeyInfo->X509Data->X509Certificate;
                $entity_IDP = $metadataIDP->attributes()->entityID;
                $singleSignOnService_IDP = $metadataIDP->IDPSSODescriptor->SingleSignOnService->attributes()->Location;
            } else {
                $metadataIDP = '';
            
                $x5092_IDP = '';
                $entity_IDP = '';
                $singleSignOnService_IDP = '';
            }
        } catch (\Exception $e) {
        }
        
        $baseUrl = $contenaire->get('urlOxaproc').'/V2';

        $settings = array (
           'strict' => true,
           'debug' => true,
           'baseurl' => $baseUrl.'/index',
           'sp' => array (
                'entityId' => $baseUrl.'/index/metadata',
                'assertionConsumerService' => array (
                   'url' => $baseUrl.'/index/ADFS',
                   'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
               ),
        
               'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
               'x509cert' => $x509,
               'privateKey' => $privateKey,
               
           ),
           'compress' => array (
               'requests' => true,
               'responses' => true
           ),
           'idp' => array (
               // Identifier of the IdP entity  (must be a URI)
               'entityId' => trim($entity_IDP ?? ''),
               'singleSignOnService' => array (
                   // URL Target of the IdP where the SP will send the Authentication Request Message
                   'url' => trim($singleSignOnService_IDP ?? ''),
                   // SAML protocol binding to be used when returning the <Response>
                   // message.  Onelogin Toolkit supports for this endpoint the
                   // HTTP-Redirect binding only
                   'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
               ),

               'x509cert' => $x5092_IDP ?? '',

               
           ),
           
        'retrieveParametersFromServer' => false,
           'security' => array (
               //'lowercaseUrlencoding' => false,
               
               /** signatures and encryptions offered */
       
               // Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
               // will be encrypted.
       
               // Indicates whether the <samlp:AuthnRequest> messages sent by this SP
               // will be signed.  [Metadata of the SP will offer this info]
               'authnRequestsSigned' => false,
               'signMetadata' => array (
                   'x509cert' => $x509,
                   'privateKey' => $privateKey
              ),


              'logoutRequestSigned' => false,

               /** signatures and encryptions required **/
       
               // Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest> and
               // <samlp:LogoutResponse> elements received by this SP to be signed.
               'wantMessagesSigned' => false,
       
               'nameIdEncrypted' => false,
               // Indicates a requirement for the <saml:Assertion> elements received by
               // this SP to be encrypted.
               'wantAssertionsEncrypted' => false,
               
       
               // Indicates a requirement for the <saml:Assertion> elements received by
               // this SP to be signed.        [The Metadata of the SP will offer this info]
               'wantAssertionsSigned' => false,
               'lowercaseUrlencoding' => false,
               'requestedAuthnContext' => false,
              
           ),
   
        );
        $samlSettings = new \OneLogin\Saml2\Settings($settings, true);
        $sp = $samlSettings->getSPData();
        $samlMetadata = \OneLogin\Saml2\Metadata::builder($sp, true, true);
        $samlMetadata = \OneLogin\Saml2\Metadata::addX509KeyDescriptors($samlMetadata, $x509);
        // $samlMetadata = \OneLogin\Saml2\Metadata::signMetadata($samlMetadata, $privateKey, $x509,
        // \RobRichards\XMLSecLibs\XMLSecurityKey::RSA_SHA256,\RobRichards\XMLSecLibs\XMLSecurityKey::RSA_SHA1);
        return $samlMetadata;
    }

    public function getLogin() :?string
    {
        return $this->login;
    }

    /**
     * @param array<mixed> $postInit
     * @return array<mixed>
     */
    public static function decode(array $postInit)
    {
        $decoded = base64_decode($postInit['SAMLResponse']);
        
        /** @var \SimpleXMLElement $xml */
        $xml = simplexml_load_string($decoded);
        $post = Utils::jsonDecode(Utils::jsonEncode($xml), true); // Normalize
        if (!is_array($post)) {
            throw new \Exception('invalid SAMLResponse');
        }

        $returnResult = [];

        foreach ($post['Assertion']['AttributeStatement']['Attribute'] as $postSearch) {
            $name = explode('/', $postSearch['@attributes']['Name']);

            $returnResult[$name[count($name)-1]] = $postSearch['AttributeValue'];
        }

        return $returnResult;
    }
}
