<?php

namespace App\Security\Hasher;

use App\SQLLogin\Exception\InvalidSQLLoginConfigurationException;
use App\SQLLogin\Exception\InvalidSQLPasswordException;
use Symfony\Component\PasswordHasher\Exception\InvalidPasswordException;
use Symfony\Component\PasswordHasher\Hasher\CheckPasswordLengthTrait;
use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface;

class PasswordEncoder implements LegacyPasswordHasherInterface
{
    use CheckPasswordLengthTrait;
    public const PASSWORD_PATTERN = 'password';
    public const SALT_PATTERN = 'salt';
    public const PEPPER_PATTERN = 'pepper';

    protected ?string $pepper;
    protected array $hashAlgoLegacy;
    protected array $securityPattern;

    public function __construct(?string $pepper, array $hashAlgoLegacy, array $securityPattern)
    {
        $this->pepper = $pepper;
        $this->hashAlgoLegacy = $hashAlgoLegacy;
        $this->securityPattern = $securityPattern;
    }

    /**
     * Pas utilisé, mais on doit le garder pour le implements
     */
    public function hash(string $plainPassword, string $salt = null): string
    {
        if ($this->isPasswordTooLong($plainPassword)) {
            throw new InvalidPasswordException();
        }

        return hash($plainPassword.$salt, $this->hashAlgoLegacy[0]);
    }

    public function verify(string $hashedPassword, string $plainPassword, string $salt = null): bool
    {
        if ('' === $plainPassword || $this->isPasswordTooLong($plainPassword)) {
            return false;
        }

        $completedPassword = $this->getPasswordToHash($plainPassword, $salt);

        foreach ($this->hashAlgoLegacy as $algo) {
            if ('ssha' === $algo && $this->compareSsha($hashedPassword, $completedPassword)) {
                return true;
            }
            if ($this->isObsoleteAlgo($algo)) {
                if (hash_equals(hash($algo, $completedPassword), $hashedPassword)) {
                    return true;
                }
            } else {
                if (password_verify($completedPassword, $hashedPassword)) {
                    return true;
                }
            }
        }
        throw new InvalidSQLPasswordException();
    }

    public function needsRehash(string $hashedPassword): bool
    {
        return false;
    }

    public function isObsoleteAlgo($algo): bool
    {
        return in_array($algo, hash_algos());
    }

    /**
     * Retourne la string à hasher en fonction du pattern indiqué
     *
     * @param mixed $plainTextPassword
     * @param mixed $salt
     *
     * @return string
     */
    protected function getPasswordToHash($plainTextPassword, $salt)
    {
        $arrayRef = [
            self::PASSWORD_PATTERN => $plainTextPassword,
            self::SALT_PATTERN => $salt,
            self::PEPPER_PATTERN => $this->pepper,
        ];

        foreach ($this->securityPattern as $term) {
            if (!isset($arrayRef[$term])) {
                throw new InvalidSQLLoginConfigurationException();
            }
        }
        $completedPlainPassword = '';
        foreach ($this->securityPattern as $term) {
            $completedPlainPassword .= $arrayRef[$term];
        }

        return $completedPlainPassword;
    }

    protected function compareSsha($hashPassword, $plainPassword)
    {
        $base_64_hash_with_salt = substr($hashPassword, 6);
        $hash_with_salt = base64_decode($base_64_hash_with_salt);
        $hash = substr($hash_with_salt, 0, 20);
        $salt = substr($hash_with_salt, 20);

        //  hash given password
        $hash_given = sha1($plainPassword.$salt, true);

        return $hash == $hash_given;
    }
}