<?php
namespace Modules\Connexion\Controller;

use Helpers\Cache\Cache;
use Helpers\Outil\Outils;
use Helpers\Outil\Roles;
use Helpers\SSO\SSO;
use Modules\Connexion\MiddlewareConnexion\SAML2;
use Modules\Connexion\MiddlewareConnexion\SSOInterne;
use Modules\Connexion\MiddlewareConnexion\Standard;
use Modules\Connexion\Service\ConnexionService;
use Modules\Connexion\Service\ControleConnexion;
use Modules\Connexion\Service\InitialisationSessionUser;
use Modules\GlobalApplication\Repository\LicenceOxalys;
use Modules\Panier\Service\OutilPanierService;
use Skeleton\Controller\Controller;
use Entity\Model\Langues;
use Helpers\Outil\Sql;
use GuzzleHttp\Psr7\Response;
use Helpers\Outil\Captcha;
use Modules\Index\Repository\Connexion;
use Modules\Index\Outil\OutilConnexion;
use Helpers\Supervision\Supervision;
use Firebase\JWT\JWT;
use Helpers\Securite\SecuriteIP;
use Modules\Index\Outil\OutilDoubleAuthentification;
use Entity\Model\Paravrac;
use Firebase\JWT\Key;
use Helpers\Session\ParseBase1;
use Helpers\Securite\Securite;
use Psr\Http\Message\ResponseInterface;
use Skeleton\Request;

class ConnexionController extends Controller
{

    public function init():void
    {
        unset($_SESSION['SAML2_CONNECT']);
    }
    
    /**
     * ssoInterneAction
     *
     * @return mixed
     */
    public function ssoInterneAction()
    {
        $token = $this->request->getAttribute('token');
        /** @phpstan-ignore-next-line */
        $bodyToken =  explode('|', Securite::decryptTokenCSRF(
            /** @phpstan-ignore-next-line */
            str_replace(
                'spluss',
                '+',
                str_replace(
                    'segals',
                    '=',
                    str_replace('sslachs', '/', $token)
                )
            ),
            true,
            true
        ));

        if ($bodyToken[0]>time()) {
            return $this->connexionAction($bodyToken[1], 'mwInterne');
        } else {
            return (new Response())->withStatus(301)->withHeader('Location', $this->contenaire->get('urlOxaproc').'/index.php');
        }
    }

    /**
     * Summary of connexionAction
     * @return mixed
     */
    public function ADFSAction()
    {
        $logout = $this->request->getAttribute('logout');
        return $this->connexionAction('', $logout? 'mwSAML2logout':'mwSAML2');
    }

    /**
     * Summary of connexionAction
     * @param mixed $request
     * @param string|bool $sessionId
     * @return mixed
     */
    public function connexionAction(?string $loginSSO = null, ?string $redirect = null, $sessionId = false, $request = null)
    {
        if ($sessionId && session_id() != $sessionId) {
            session_destroy();
            session_id((string)$sessionId);
            session_name('__OxaProc-PHPSESSID');
            session_start();
        }

        $trad = $this->getTranslationService();

        if ($request) {
            $this->request = $request;
        }

        $post = $this->request->getParsedBody(true);
        $connexionService = new ConnexionService($this->pdo, $this->pdoOxaFT, $this->pdoStorage, $trad, $this->router, $this->JWTKEY);

        $debutConnexion = microtime(true);
        $messageErreur  = [];

        /** get */
        if (is_null($redirect)) {
            $redirect   = ($this->request->getAttribute('redirect')     == 'NULL'? '':$this->request->getAttribute('redirect'));
        }

        $errorUrl   = ($this->request->getAttribute('error')        == 'NULL'? '':$this->request->getAttribute('error'));
        $access     = ($this->request->getAttribute('access')       == 'NULL'? '':$this->request->getAttribute('access'));
        $id         = ($this->request->getAttribute('id')           == 'NULL'? '':$this->request->getAttribute('id'));
        $profil     = ($this->request->getAttribute('profil')       == 'NULL'? '':$this->request->getAttribute('profil'));
        $menu       = ($this->request->getAttribute('menu')         == 'NULL'? '':$this->request->getAttribute('menu'));
        $refDemande = ($this->request->getAttribute('refDemande')   == 'NULL'? '':$this->request->getAttribute('refDemande'));
        
        if ($access == 'mail' && $profil == 'Dem') {
            $_SESSION['ID_TMP']         = $id;
            $_SESSION['REF_DEMANDE']    = $refDemande;
            return $this->redirect('Utilisateur.Profile.suiviCompte');
        }

        $connexionOutil = new OutilConnexion($this->pdo, $this->pdoOxaFT, $this->contenaire);

        /** Init fonction */
        if (Outils::isHttps()) {
            session_regenerate_id();
        }

        $captchaActif = $connexionService->captchaActif();
        OutilPanierService::checkRechercheActif($this->pdoOxaFT);
        
        $connexionOutil->getEnv();

        $multiLangueActif = sql::boolSql(sql::paravrac($this->pdo, 'PARAMOXA', 'MULTILNGACTIF'));
        $infoEnvironnement = $connexionOutil->controlVersion($this->getVersion());
  
        /** REDIRECTION */
        if ($redirect == 'ssointerne') {
            return $this->redirect($this->contenaire->get('urlOxaprocSso').'/index.php?passage=1');
        } elseif ($redirect == 'mwInterne') {
            $connexionMiddleware = new SSOInterne($this->pdo, $trad);
            $connexionMiddleware->setLogin($loginSSO);
            $connexionMiddleware->setError($errorUrl);
        } elseif ($redirect == 'mwSAML2' || $redirect == 'mwSAML2logout') {
            $connexionMiddleware = new SAML2($this->pdo, $trad);
            $connexionMiddleware->setLogout($redirect == 'mwSAML2logout');
            $connexionMiddleware->setContainer($this->contenaire);
        } else {
            $connexionMiddleware = new Standard($this->pdo, $trad);
            $connexionMiddleware->setCaptchaActif($captchaActif);
        }

        if ($_SESSION['Session_Login']??'') {
            $arrayRerdirect = $connexionOutil->redirection($access, $id, $profil, $refDemande);
    
            $codeUrl    = $arrayRerdirect['codeUrl']??'';
            $paramUrl   = $arrayRerdirect['paramUrl']??[];
            $postUrl    = $arrayRerdirect['postUrl']??[];
            
            if ($codeUrl) {
                if ($postUrl) {
                    return $this->redirect($codeUrl, $paramUrl, $postUrl);
                } else {
                    return $this->redirect($codeUrl, $paramUrl);
                }
            }
        }

        /** GESTION DE LA SOUMISION DE LA CONNEXION */
        // Reception des informations
        $connexionMiddleware->authentificationGlobal($post);
        $login              = $connexionMiddleware->getLogin();
        $password           = $connexionMiddleware->getPassword();
        $languePost         = $connexionMiddleware->getLangue();
        $rendererAuthen     = $connexionMiddleware->renderer($this->renderer);

        $errorEnvironnement = $connexionService->controleEnvironnement($infoEnvironnement);

        if ($errorEnvironnement['bloquant']) {
            $messageErreur = array_merge($messageErreur, $errorEnvironnement['erreur']);
        } elseif ($rendererAuthen) {
            return $rendererAuthen;
        } elseif ($connexionMiddleware->authenSucess() && $login) {
            $languePost = (string)($languePost==''?  Roles::droit($this->pdo, 'LNGBDD', 'PARAMOXA', true):$languePost);
            $securiteIP = new SecuriteIP($this->pdo, $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'], $login, (isset($_SESSION['ContexteMobile'])?'M':'P'));
            
            if (!$securiteIP->IPValid()) {
                return (new Response())->withStatus(403)->withHeader('Location', $this->router->generateUri('Index.Index.forbidden'));
            }

            Supervision::setHeureDebut();

            $userTry = (new Connexion($this->pdo))->getInfoUser($login);
            $connexionService->setupLangue($post, $languePost);
            
            $connexionControle = new ControleConnexion($this->pdo, $trad);
            $connexionControle->setUser($userTry);
            $connexionControle->setMobilite($_SESSION['ContexteMobile']??false);
            if ($connexionControle->connexionOk()) {
                unset($_SESSION['connexion']);

                if ($connexionMiddleware->getTypeConnexion() == 'STD' && strtoupper($userTry['CODE']) != 'OXAUSER') {
                    $connexionService->setupMobilite($post, $login, $password);
                    $doubleAuth = $connexionService->doubleAuth($post, $login);
                    if ($doubleAuth) {
                        return $doubleAuth;
                    }
                }

                if ($access != 'mail') {
                    // On supprime les infos de redirections si connexion directe
                    unset($_SESSION['REDIRECTION']);
                    unset($_SESSION['ID_TMP']);
                    unset($_SESSION['PROFIL_TMP']);
                    unset($_SESSION['MENU_TMP']);
                    unset($_SESSION['REF_DEMANDE']);
                }

                (new InitialisationSessionUser($this->pdo, $this->pdoOxaFT, $trad))->initSessionUser($login, $languePost);

                (new Connexion($this->pdo))->trackConnexionIn($_SESSION['Session_Login']);
                Cache::surchargeDeleteUcache($_SESSION['Session_Login'].(new ParseBase1())->getBroker());

                if (isset($_SESSION['ContexteMobile']) && $_SESSION['ContexteMobile']==true) {
                    if ($connexionControle->connexionMobility()) {
                        $connexionService->initProfilUser($connexionControle->connexionOxaproc(), $userTry);
                        Supervision::executionAsynchrone($this->router->generateUri('GlobalApplication.Outil.supervision'));
    
                        $utilisateurSQL = new \Modules\GlobalApplication\Repository\Utilisateur($this->pdo);
                        $user = $utilisateurSQL->rqInfoUserByLogin($_SESSION['Session_Login']);
                        $finConnexion = microtime(true);
                        $tempo = (int) (($finConnexion - $debutConnexion) * 1000);
                        sql::logAudit($this->pdo, 'LOGIN', 'ENTREE', 'CONNEXION OK', '', '', $tempo);
                        
                        if ($user['ExpirationMdP'] == 1 && $user['GestionMotdePasse']) {
                            return $this->redirect('Index.Password.changementPassword');
                        }
                        
                        return $this->redirect('Connexion.MobilityConnexion.returnResponse');
                    } else {
                        $messageErreur[] = $trad->t('Aucune application n\'est disponible pour votre compte.');
                    }
                } elseif ($connexionControle->connexionOxaproc() || $connexionControle->connexionSRM()) {
                    $connexionService->initProfilUser($connexionControle->connexionOxaproc(), $userTry);
                    Supervision::executionAsynchrone($this->router->generateUri('GlobalApplication.Outil.supervision'));

                    if ($connexionControle->connexionOxaproc()) {
                        $utilisateurSQL = new \Modules\GlobalApplication\Repository\Utilisateur($this->pdo);
                        $user = $utilisateurSQL->rqInfoUserByLogin($_SESSION['Session_Login']);
                        $_SESSION['id_user'] = $user['id_user'];
    
                        $finConnexion = microtime(true);
                        $tempo = (int) (($finConnexion - $debutConnexion) * 1000);
                        sql::logAudit($this->pdo, 'LOGIN', 'ENTREE', 'CONNEXION OK', '', '', $tempo);
                        
                        if ($user['ExpirationMdP'] == 1 && $user['GestionMotdePasse']) {
                            return $this->redirect('Index.Password.changementPassword');
                        }
                    } else {
                        return $this->redirect('Index.Index.SRMIndex');
                    }

                    $arrayRerdirect = [
                        'access'        => $access,
                        'id'            => $id,
                        'profil'        => $profil,
                        'menu'          => $menu,
                        'refDemande'    => $refDemande,
                    ];

                    return $connexionService->gestionRedirection($userTry, $arrayRerdirect, $this->contenaire);
                } else {
                    $finConnexion = microtime(true);
                    $tempo = (int) (($finConnexion - $debutConnexion) * 1000);
                    sql::logAudit($this->pdo, 'LOGIN', 'ENTREE', 'ECHOUE', '', '', $tempo);
                    
                    $messageErreur[] = $trad->t('Aucune application n\'est disponible pour votre compte.');
                }
            } else {
                $erreurControle = $connexionControle->getError();
                $messageErreur = array_merge($messageErreur, $erreurControle);
            }
        } else {
            $messageErreur = array_merge($messageErreur, $connexionMiddleware->getError());
        }
    
        if (!$errorEnvironnement['bloquant'] && $errorEnvironnement['erreur']) {
            $messageErreur = array_merge($messageErreur, $errorEnvironnement['erreur']);
        }

        if (OutilDoubleAuthentification::doubleAuthActive($this->pdo)['dbleAuthActive'] != true) {
            $idInput        =  Sql::guidGenerate(true);
            $mdpInput       =  Sql::guidGenerate(true);
            $tokenInput     =  Sql::guidGenerate(true);
            $langueInput    =  Sql::guidGenerate(true);

            $_SESSION['connexion']['idInput']       = $idInput;
            $_SESSION['connexion']['mdpInput']      = $mdpInput;
            $_SESSION['connexion']['tokenInput']    = $tokenInput;
            $_SESSION['connexion']['langueInput']   = $langueInput;
        }

        $connexionOutil->initParametrage($this->pdo);

        if ($captchaActif) {
            (new Captcha())->createCaptcha('login', 'captchaLogin');
        }

        if (isset($_COOKIE['MOBILITY_TOKEN'])) {
            $cookieJwtDecode = (array) Jwt::decode($_COOKIE['MOBILITY_TOKEN'], new Key($this->JWTKEY, 'HS256'));

            if ($cookieJwtDecode['agent'] !== $_SESSION['MobilityIdDevice']) {
                $cookieJwtDecode = [];
            }

            if (isset($cookieJwtDecode['password'])) {
                $cookieJwtDecode['password'] = Securite::decodeChiffrageFromMagic($cookieJwtDecode['password']);
            }
        }

        return $this->renderer->render('@connexion/connexion', [
            'multiLangueActif'    => $multiLangueActif,
            'langues'             => (new Langues($this->pdo))->listChoice(),
            'idInput'             => $idInput ?? '',
            'mdpInput'            => $mdpInput ?? '',
            'tokenInput'          => $tokenInput ?? '',
            'langueInput'         => $langueInput ?? '',
            'messageErreur'       => implode('<br />', $messageErreur),
            'login'               => $login==''? $id:$login,
            'langue'              => $languePost? $languePost:$_SESSION['Session_Langue'],
            'autodeclaration'     => (new Paravrac($this->pdo))->find(['TYPEPARV' => 'OXAPROC', 'CODEPARV' => 'LOGIN_MODE_FONCTION'])['LIB01PARV'],
            'captchaActif'        => $captchaActif,
            'ContexteMobile'      => $_SESSION['ContexteMobile']??null,
            'MobilityLogin'       => $_SESSION['MobilityLogin']??null,
            
            // SSO information
            'infoSSO'             => (new SSO($this->router, $this->contenaire, $this->pdo))->getInfoSSO(),
            'cookieJwt'           => $cookieJwtDecode['password'] ?? false,
            'rememberMe'          => !empty($_SESSION['MobilityIdDevice'])
        ]);
    }

    public function changementLangueAction() : string
    {
        $post = $this->request->getParsedBody(true);
        $langueInputSave = 'langue'.$_SESSION['connexion']['langueInput'];
        
        $_SESSION['Session_Langue'] = $post[$langueInputSave];
        $arr_cookie_options = array (
            'expires' => time() + 60 * 60 * 24 * 365 * 10,
            //'secure' => true,     // or false
            'httponly' => true,    // or false
            //'samesite' => 'None' // None || Lax  || Strict
            );
        if (Outils::isHttps()) {
            $arr_cookie_options['samesite'] = 'None';
            $arr_cookie_options['secure'] = true;
        }
        setcookie('OxaprocDeflangue', $_SESSION['Session_Langue'], $arr_cookie_options);

        return '';
    }

    public function metadataAction():mixed
    {
        $post = $this->request->getParsedBody(true);

        header("Content-type: text/xml");
        $connexionMiddleware = new SAML2($this->pdo, $this->getTranslationService());
        $connexionMiddleware->setContainer($this->contenaire);
        return $connexionMiddleware->getMetada($post);
    }

    public function connectMagicSaml2Action() : ResponseInterface
    {
        $uuid =  $this->request->getAttribute('uuid');

        /**
         * Create session for context
         */
        $_SESSION[SAML2::NAMEMAGICSESSION] = $uuid;

        return  $this->connexionAction(null, 'mwSAML2');
    }
}
