<?php

declare(strict_types=1);

namespace App\Tests\Functional\Module\Core;

use App\Module\Core\Domain\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

/**
 * Covers the soft-delete behaviour: deleting a user archives it (the row is
 * kept so referencing tasks/time entries still serialize), archived users are
 * hidden from the default collection but an admin can list and restore them.
 *
 * @internal
 */
final class UserArchiveApiTest extends WebTestCase
{
    public function testDeleteArchivesUserInsteadOfRemovingIt(): void
    {
        $client = self::createClient();
        $em     = self::getContainer()->get(EntityManagerInterface::class);
        $target = $this->createUser($em, 'archive-target-'.uniqid());
        $em->flush();
        $targetId = $target->getId();

        $this->loginAdmin($client);
        $client->request('DELETE', '/api/users/'.$targetId);

        self::assertResponseStatusCodeSame(204);

        $em->clear();
        $reloaded = $em->getRepository(User::class)->find($targetId);
        self::assertInstanceOf(User::class, $reloaded, 'Row must still exist (soft delete)');
        self::assertTrue($reloaded->isArchived(), 'User must be flagged archived');
        self::assertNull($reloaded->getApiToken(), 'API token must be cleared on archive');
    }

    public function testAdminCannotArchiveOwnAccount(): void
    {
        $client = self::createClient();
        $em     = self::getContainer()->get(EntityManagerInterface::class);
        $this->loginAdmin($client);
        $adminId = $this->userId('admin');

        $client->request('DELETE', '/api/users/'.$adminId);

        self::assertResponseStatusCodeSame(403);

        $em->clear();
        $admin = $em->getRepository(User::class)->find($adminId);
        self::assertFalse($admin->isArchived(), 'Admin must not have archived itself');
    }

    public function testArchivedUserIsHiddenFromDefaultCollection(): void
    {
        $client   = self::createClient();
        $username = $this->createArchivedUser();

        $this->loginAdmin($client);
        $client->request('GET', '/api/users', server: ['HTTP_ACCEPT' => 'application/json']);

        self::assertResponseIsSuccessful();
        $usernames = array_column(json_decode($client->getResponse()->getContent(), true), 'username');
        self::assertNotContains($username, $usernames, 'Archived user must not appear in the default list');
    }

    public function testAdminCanListArchivedUsersViaFilter(): void
    {
        $client   = self::createClient();
        $username = $this->createArchivedUser();

        $this->loginAdmin($client);
        $client->request('GET', '/api/users?archived=true', server: ['HTTP_ACCEPT' => 'application/json']);

        self::assertResponseIsSuccessful();
        $usernames = array_column(json_decode($client->getResponse()->getContent(), true), 'username');
        self::assertContains($username, $usernames, 'Admin must be able to list archived users via ?archived=true');
    }

    public function testAdminCanRestoreUserViaPatch(): void
    {
        $client = self::createClient();
        $em     = self::getContainer()->get(EntityManagerInterface::class);

        $user = $this->createUser($em, 'restore-target-'.uniqid());
        $user->setArchived(true);
        $em->flush();
        $userId = $user->getId();
        $em->clear();

        $this->loginAdmin($client);
        $client->request('PATCH', '/api/users/'.$userId, server: [
            'CONTENT_TYPE' => 'application/merge-patch+json',
        ], content: json_encode(['archived' => false]));

        self::assertResponseIsSuccessful();

        $em->clear();
        $reloaded = $em->getRepository(User::class)->find($userId);
        self::assertFalse($reloaded->isArchived(), 'Admin PATCH must be able to un-archive a user');
    }

    private function createArchivedUser(): string
    {
        $em       = self::getContainer()->get(EntityManagerInterface::class);
        $username = 'archived-'.uniqid();
        $user     = $this->createUser($em, $username);
        $user->setArchived(true);
        $em->flush();
        $em->clear();

        return $username;
    }

    private function createUser(EntityManagerInterface $em, string $username): User
    {
        $user = new User();
        $user->setUsername($username);
        $user->setPassword('x');
        $user->setRoles(['ROLE_USER']);
        $em->persist($user);

        return $user;
    }

    private function loginAdmin(KernelBrowser $client): void
    {
        $em   = self::getContainer()->get(EntityManagerInterface::class);
        $user = $em->getRepository(User::class)->findOneBy(['username' => 'admin']);
        self::assertInstanceOf(User::class, $user);
        $client->loginUser($user);
    }

    private function userId(string $username): int
    {
        $em   = self::getContainer()->get(EntityManagerInterface::class);
        $user = $em->getRepository(User::class)->findOneBy(['username' => $username]);
        self::assertInstanceOf(User::class, $user);

        return $user->getId();
    }
}
