<?php

namespace Modules\Connexion\MiddlewareConnexion;

use Entity\Model\Hastutil;
use Entity\Model\MUChif;
use Entity\Model\ParamDico;
use Entity\Model\UtilBloque;
use Entity\Model\UtilTentCnx;
use Helpers\Outil\Captcha;
use Helpers\Outil\Roles;
use Helpers\Outil\Sql;
use Helpers\Session\ParseBase1;
use Helpers\Trad\ITrad;
use Helpers\Validator\Validator;
use Modules\Connexion\Repository\ControleConnexionRepo;
use Modules\Connexion\Service\ControlePwdService;
use Skeleton\Renderer\RendererInterface;
use Psr\Container\ContainerInterface;

class Standard extends AbsConnexion implements InterfaceConnexion
{
    /**
     * @var array<mixed>
     */
    private array $post;
    private bool $captchaActif = false;

    public function __construct(public \PDO $pdo, private ITrad $trad)
    {
        $this->typeConnexion = 'STD';
    }

    public function setCaptchaActif(bool $captchaActif) :void
    {
        $this->captchaActif = $captchaActif;
    }
    
    /**
     * @param array<mixed> $post
     */
    public function authentification(array $post = []) :void
    {
        $this->post = $post;

        $validator = new Validator($this->pdo, $this->trad);

        if ($post) {
            if ($this->captchaActif) {
                $validator->captcha((new Captcha())->getCaptcha('login')['code'], $post['login']);
    
                if (!$validator->isValid()) {
                    $this->error[] = $validator->getError();
                }
            }

            $login = $this->post['ID'.($_SESSION['connexion']['idInput'] ?? '')]??'';

            $this->controleLogin($login);
        }
    }

    public function getPassword():string|null
    {
        return $this->post['MDP'.($_SESSION['connexion']['mdpInput'] ?? '')]??'';
    }
    public function getLangue():string|null
    {
        return $this->post['langue'.($_SESSION['connexion']['langueInput'] ?? '')]??'';
    }

    private function controleLogin(string $login = null):void
    {
        $loginTry = (new ControleConnexionRepo($this->pdo))->getInfoUser($login);
        
        $oxaUserOpen = Roles::droit($this->pdo, 'ACCESEDITEUROUVERT');
        if ((strtoupper($login) == 'OXAUSER' && $oxaUserOpen) || strtoupper($login) == 'ADMIN') {
            $this->login = $login;
            $this->pwdOxaUser();
        } elseif ($loginTry) {
            $this->login = $loginTry['CODE'];
            if ($loginTry['TYP_LOGIN'] == '' || $loginTry['TYP_LOGIN'] == 'H') {
                $controlePwd = $this->controlePassword();
            } else {
                $controlePwd = $this->activeDirectory($loginTry['NOMDOM'], $loginTry['TYP_LOGIN'] == 'L');
            }

            if (!$controlePwd) {
                $this->blocageLogin($this->login);
            } else {
                $this->compteBloquer($login);
                (new UtilTentCnx($this->pdo))->delete(['CODE' => $this->login]);
            }
        } else {
            $this->error[] = $this->trad->t('Identifiant ou mot de passe incorrect.');
        }
    }

    private function activeDirectory(string $domain, bool $ldap = false):bool
    {
        /*if (!$this->controleMagic()) {
            $this->error[] = $this->trad->t('Identifiant ou mot de passe incorrect.');
            return false;
        }

        return true;*/

        $paramDico = (new ParamDico($this->pdo))->find([
            'TYPEDICO'  => $ldap? 'PARAM_SERVEURS_LDAD':'PARAM_SERVEURS_AD',
            'ID1'       => $domain
        ]);

        $ldap_server    = $paramDico['ID2'];
        /** @phpstan-ignore-next-line */
        $ldap_port      = (int)$paramDico['LIBELLE']??389;

        $user_pseudo    = $this->post['ID'.($_SESSION['connexion']['idInput'] ?? '')]??'';
        $user_password  = $this->post['MDP'.($_SESSION['connexion']['mdpInput'] ?? '')]??'';
        // --- Connexion au serveur AD
        if ($_SESSION['Session_NumClient'] == '13') {
            $ad = ldap_connect($ldap_server, $ldap_port);
        } else {
            $ad = ldap_connect("LDAPS://$ldap_server:$ldap_port");
        }
        if ($ad) {
            // Définissons quelques variables...
            ldap_set_option($ad, LDAP_OPT_PROTOCOL_VERSION, 3);
            ldap_set_option($ad, LDAP_OPT_REFERRALS, 0);
            // On tente l'authentification
            @$bd = ldap_bind($ad, "$user_pseudo@$domain", "$user_password");
            if (!$bd) {
                $this->error[] = $this->trad->t('Identifiant ou mot de passe incorrect.');
                return false;
            }
    
            ldap_close($ad);
        } else {
            $this->error[] = $this->trad->t('Une erreur est survenue lors de la connexion'). ' '.($ldap? 'LDAP':'Active directory');
            return false;
        }

        return true;
    }

    private function controlePassword():bool
    {
        $retourPwd = (new ControlePwdService($this->pdo, $this->login))->checkPassword($this->getPassword());

        if ($retourPwd) {
            return true;
        } else {
            if (mb_strlen($this->getPassword()) == 30) {
                return $this->controleMagic();
            } else {
                $this->error[] = $this->trad->t('Identifiant ou mot de passe incorrect.');
            }
        }

        return false;
    }

    private function controleMagic():bool
    {
        $Wsdl = new \Modules\Index\WebService\Utilisateur($this->pdo);
        $xmlResult = $Wsdl->WsdlIdent($this->login, $this->getPassword(), $this->getLangue(), []);

        $xml = simplexml_load_string($xmlResult->responseData);
        $json = (string)json_encode($xml);
        $resultat = json_decode($json, true);

        if ($resultat['ConnexionReussie'] == 'true') {
            return true;
        }

        return false;
    }

    private function pwdOxaUser():void
    {
        if (!$this->controleMagic()) {
            $this->error[] = $this->trad->t('Identifiant ou mot de passe incorrect.');
        }
 
        /*$mdpOxaUser = "";

        $arrayDayOfTheWeek = [
            '1' => 'Monday',
            '2' => 'Tuesday',
            '3' => 'Wednesday',
            '4' => 'Thursday',
            '5' => 'Friday',
            '6' => 'Saturday',
            '7' => 'Sunday',
        ];

        $arrayJoursDeLaSemaine = [
            '1' => 'Lundi',
            '2' => 'Mardi',
            '3' => 'Mercredi',
            '4' => 'Jeudi',
            '5' => 'Vendredi',
            '6' => 'Samedi',
            '7' => 'Dimanche',
        ];

        $arrayOxaUser = [
            '<NUM_CLT>'     => str_pad((string)$_SESSION['Session_NumClient'], 3, '0', STR_PAD_LEFT),
            '<NUM_ENV>'     => (new ParseBase1())->getBroker(),

            '<YY>'          => date('j'),
            '<MM>'          => date('m'),
            '<DD>'          => date('d'),

            '<PLJ_MAJ_FR>'  => substr(strtoupper($arrayJoursDeLaSemaine[date('w')]), 0, 1),
            '<PLJ_MAJ_EN>'  => substr(strtoupper($arrayDayOfTheWeek[date('w')]), 0, 1),

            '<PLJ_MIN_FR>'  => substr(strtolower($arrayJoursDeLaSemaine[date('w')]), 0, 1),
            '<PLJ_MIN_EN>'  => substr(strtolower($arrayDayOfTheWeek[date('w')]), 0, 1),

            '<NJ_MAJ_FR>'   => strtoupper($arrayJoursDeLaSemaine[date('w')]),
            '<NJ_MAJ_EN>'   => strtoupper($arrayDayOfTheWeek[date('w')]),

            '<NJ_MIN_FR>'   => strtolower($arrayJoursDeLaSemaine[date('w')]),
            '<NJ_MIN_EN>'   => strtolower($arrayDayOfTheWeek[date('w')]),
        ];

        foreach ($arrayOxaUser as $k => $v) {
            $mdpOxaUser = str_replace($k, $v, $mdpOxaUser);
        }

        if ($this->post['MDP'.($_SESSION['connexion']['mdpInput'] ?? '')] != $mdpOxaUser) {
            $this->error[] = $this->trad->t('Identifiant ou mot de passe incorrect.');
        }*/
    }

    private function blocageLogin(string $login):void
    {
        $utilTentCnx = new UtilTentCnx($this->pdo);
        $tryLogin = $utilTentCnx->find($login);

        if ($tryLogin) {
            $nombreTentative = $tryLogin['NBRE_TENT']+1;
            $utilTentCnx->update([
                'NBRE_TENT' => $nombreTentative
            ], $login);
        } else {
            $nombreTentative = 1;
            $utilTentCnx->insert([
                'CODE'          => $login,
                'NBRE_TENT'     => $nombreTentative,
            ]);
        }
        $nbMaxTtry = (int)sql::paravrac($this->pdo, 'PARAMGEN', 'MDPNBCONNECT');

        if ($nombreTentative >= $nbMaxTtry) {
            (new UtilBloque($this->pdo))->insert([
                'CODE'          => $login,
                'DATE_BLOC'     => date('Ymd'),
                'HEURE_BLOC'    => date('His'),
                'TXT_BLOC'      => 'Dépassement du nombre de tentatives de connexion.',
                'ORIGINE'       => 'PROC',
                'ADR_IP'        => $_SERVER['REMOTE_ADDR'],
                'OS_USER'       => ''
            ]);
        }

        $this->compteBloquer($login);
    }

    private function compteBloquer(string $login):void
    {
        $utilTentCnx = new UtilTentCnx($this->pdo);

        $infoBlocage = (new UtilBloque($this->pdo))->find($login);
        $innactiveAccount = sql::paravrac($this->pdo, 'PARAMGEN', 'MDP_INAC_HH');
        if ($infoBlocage) {
            $blocage = false;
            if ($innactiveAccount != 'P') {
                $strToTime = (int)strtotime($infoBlocage['DATE_BLOC'].' '.$infoBlocage['HEURE_BLOC'].' + '.(int)$innactiveAccount.' hour');
                $strToTimeNow = (int) strtotime(date('Ymd His'));

                if ($strToTime > $strToTimeNow) {
                    $blocage = true;
                } else {
                    $utilTentCnx->delete(['CODE' => $login]);
                    (new UtilBloque($this->pdo))->delete($login);
                }
            } else {
                $blocage = true;
            }
    
            if ($blocage == true) {
                if ($innactiveAccount == 'P') {
                    $startMessage = $this->trad->t('Compte utilisateur bloqué.');
                } else {
                    $startMessage = $this->trad->t('Compte utilisateur bloqué temporairement.');
                }
                unset($this->error);
                $this->error[] = $startMessage."<br> ".$this->trad->t('Cause').' : '.$this->trad->t($infoBlocage['TXT_BLOC']);
            }
        }
    }
}
