<?php

namespace App\Tests\Controller\Admin;

use App\Controller\Admin\ProfilController;
use App\Entity\User;
use App\Service\KeycloakAdminService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Twig\Environment;

class ProfilControllerTest extends TestCase
{
    private function createContainer(?User $user = null): ContainerInterface
    {
        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(RequestStack::class);
        $stack->method('getSession')->willReturn($session);

        $twig = $this->createStub(Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $router = $this->createStub(RouterInterface::class);
        $router->method('generate')->willReturn('/admin/profil');

        $tokenStorage = $this->createStub(TokenStorageInterface::class);
        if (null !== $user) {
            $token = $this->createStub(TokenInterface::class);
            $token->method('getUser')->willReturn($user);
            $tokenStorage->method('getToken')->willReturn($token);
        }

        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturn(true);
        $container->method('get')->willReturnMap([
            ['twig', $twig],
            ['router', $router],
            ['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
            ['security.token_storage', $tokenStorage],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class)],
        ]);

        return $container;
    }

    private function createUser(): User
    {
        $user = new User();
        $user->setEmail('profil@test.com');
        $user->setFirstName('Jean');
        $user->setLastName('Profil');
        $user->setPassword('hashed_old');

        return $user;
    }

    public function testIndex(): void
    {
        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer());

        $response = $controller->index();
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testPasswordWrongCurrent(): void
    {
        $user = $this->createUser();

        $hasher = $this->createStub(UserPasswordHasherInterface::class);
        $hasher->method('isPasswordValid')->willReturn(false);

        $request = new Request([], ['current_password' => 'wrong', 'new_password' => 'newpass12', 'confirm_password' => 'newpass12']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->password($request, $hasher, $this->createStub(EntityManagerInterface::class), $this->createStub(KeycloakAdminService::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testPasswordTooShort(): void
    {
        $user = $this->createUser();

        $hasher = $this->createStub(UserPasswordHasherInterface::class);
        $hasher->method('isPasswordValid')->willReturn(true);

        $request = new Request([], ['current_password' => 'correct', 'new_password' => 'short', 'confirm_password' => 'short']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->password($request, $hasher, $this->createStub(EntityManagerInterface::class), $this->createStub(KeycloakAdminService::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testPasswordMismatch(): void
    {
        $user = $this->createUser();

        $hasher = $this->createStub(UserPasswordHasherInterface::class);
        $hasher->method('isPasswordValid')->willReturn(true);

        $request = new Request([], ['current_password' => 'correct', 'new_password' => 'newpass12', 'confirm_password' => 'different']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->password($request, $hasher, $this->createStub(EntityManagerInterface::class), $this->createStub(KeycloakAdminService::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testPasswordSuccessNoKeycloak(): void
    {
        $user = $this->createUser();
        $user->setTempPassword('old_tmp');

        $hasher = $this->createStub(UserPasswordHasherInterface::class);
        $hasher->method('isPasswordValid')->willReturn(true);
        $hasher->method('hashPassword')->willReturn('hashed_new');

        $request = new Request([], ['current_password' => 'correct', 'new_password' => 'newpass12', 'confirm_password' => 'newpass12']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->password($request, $hasher, $this->createStub(EntityManagerInterface::class), $this->createStub(KeycloakAdminService::class));
        $this->assertSame(302, $response->getStatusCode());
        $this->assertSame('hashed_new', $user->getPassword());
        $this->assertFalse($user->hasTempPassword());
    }

    public function testPasswordSuccessWithKeycloak(): void
    {
        $user = $this->createUser();
        $user->setKeycloakId('kc-123');

        $hasher = $this->createStub(UserPasswordHasherInterface::class);
        $hasher->method('isPasswordValid')->willReturn(true);
        $hasher->method('hashPassword')->willReturn('hashed_new');

        $request = new Request([], ['current_password' => 'correct', 'new_password' => 'newpass12', 'confirm_password' => 'newpass12']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->password($request, $hasher, $this->createStub(EntityManagerInterface::class), $this->createStub(KeycloakAdminService::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testPasswordKeycloakError(): void
    {
        $user = $this->createUser();
        $user->setKeycloakId('kc-err');

        $hasher = $this->createStub(UserPasswordHasherInterface::class);
        $hasher->method('isPasswordValid')->willReturn(true);
        $hasher->method('hashPassword')->willReturn('hashed_new');

        $kc = $this->createStub(KeycloakAdminService::class);
        $kc->method('resetPassword')->willThrowException(new \RuntimeException('KC error'));

        $request = new Request([], ['current_password' => 'correct', 'new_password' => 'newpass12', 'confirm_password' => 'newpass12']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->password($request, $hasher, $this->createStub(EntityManagerInterface::class), $kc);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateEmptyFields(): void
    {
        $user = $this->createUser();

        $request = new Request([], ['firstName' => '', 'lastName' => '', 'email' => '']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->update($request, $this->createStub(EntityManagerInterface::class), $this->createStub(KeycloakAdminService::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateSuccessNoKeycloak(): void
    {
        $user = $this->createUser();

        $request = new Request([], ['firstName' => 'Updated', 'lastName' => 'Name', 'email' => 'new@test.com']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->update($request, $this->createStub(EntityManagerInterface::class), $this->createStub(KeycloakAdminService::class));
        $this->assertSame(302, $response->getStatusCode());
        $this->assertSame('Updated', $user->getFirstName());
    }

    public function testUpdateSuccessWithKeycloak(): void
    {
        $user = $this->createUser();
        $user->setKeycloakId('kc-upd');

        $request = new Request([], ['firstName' => 'Up', 'lastName' => 'Dated', 'email' => 'up@test.com']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->update($request, $this->createStub(EntityManagerInterface::class), $this->createStub(KeycloakAdminService::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testUpdateKeycloakError(): void
    {
        $user = $this->createUser();
        $user->setKeycloakId('kc-fail');

        $kc = $this->createStub(KeycloakAdminService::class);
        $kc->method('updateUser')->willThrowException(new \RuntimeException('KC error'));

        $request = new Request([], ['firstName' => 'A', 'lastName' => 'B', 'email' => 'a@b.com']);
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->update($request, $this->createStub(EntityManagerInterface::class), $kc);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testAvatarNoFile(): void
    {
        $user = $this->createUser();

        $request = new Request();
        $request->setMethod('POST');

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->avatar($request, $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testAvatarSuccess(): void
    {
        $user = $this->createUser();

        $tmpFile = tempnam(sys_get_temp_dir(), 'avatar_');
        file_put_contents($tmpFile, 'fake-image');
        $file = new UploadedFile($tmpFile, 'avatar.png', 'image/png', null, true);

        $request = new Request();
        $request->setMethod('POST');
        $request->files->set('avatar', $file);

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->avatar($request, $this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());

        @unlink($tmpFile);
    }

    public function testAvatarDelete(): void
    {
        $user = $this->createUser();

        $controller = new ProfilController($this->createStub(LoggerInterface::class));
        $controller->setContainer($this->createContainer($user));

        $response = $controller->avatarDelete($this->createStub(EntityManagerInterface::class));
        $this->assertSame(302, $response->getStatusCode());
        $this->assertNull($user->getAvatar());
    }
}
