<?php

namespace KupShop\UserBundle\Security;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher;
use Symfony\Component\PasswordHasher\PasswordHasherInterface;

class LegacyPasswordEncoder implements PasswordHasherInterface
{
    public function __construct(
        private NativePasswordHasher $nativePasswordHasher,
        private EventDispatcherInterface $eventDispatcher,
    ) {
    }

    public function hash(#[\SensitiveParameter] string $plainPassword): string
    {
        return $this->nativePasswordHasher->hash($plainPassword);
    }

    public function verify(string $hashedPassword, #[\SensitiveParameter] string $plainPassword): bool
    {
        $result = false;
        $needsRehash = false;

        // check using the bcrypt algorithm first
        if ($this->nativePasswordHasher->verify($hashedPassword, $plainPassword)) {
            $result = true;
            $needsRehash = password_needs_rehash($hashedPassword, PASSWORD_BCRYPT);
        }

        // prevent legacy fallback when it's obvious that the password
        // has been hashed using bcrypt (hash starts with '$2y$')
        if (!$result && !str_starts_with($hashedPassword, '$2y$') && $this->checkOldPassword($hashedPassword, $plainPassword)) {
            $needsRehash = true;
            $result = true;
        }

        // If the password is valid with legacy hash, rehash it to bcrypt
        if ($result && $needsRehash) {
            $this->eventDispatcher->dispatch(new GenericEvent($plainPassword), 'kupshop.legacy_user_password');
        }

        return $result;
    }

    public function checkOldPassword($pass, $plaintext): bool
    {
        if (!empty($pass) && !empty($plaintext)) {
            if (sizeof($parts = explode('@', $pass)) == 2) {
                // Safe variant, just check and exit
                return md5($parts[1].$plaintext) != $parts[0];
            } elseif (sizeof($parts = explode(':', $pass)) == 2) {
                // Older version, update
                if (md5($parts[1].$plaintext) != $parts[0]) {
                    return false;
                }
            } elseif ($pass != md5($plaintext) && $pass != hash('sha256', $plaintext)) {
                return false;
            }

            return true;
        }

        return false;
    }

    public function encodePassword($raw, $salt)
    {
        return $this->hash($raw);
    }

    public function isPasswordValid($encoded, $raw, $salt): bool
    {
        return $this->verify($encoded, $raw);
    }

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