<?php

declare(strict_types=1);

namespace App\Tests\Functional\Administration\Api;

use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Administration\Application\Service\Cache\PaginatedQueryCache;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\ImageRightsStatus;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\StatutCompte;
use App\Administration\Domain\Model\User\User;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Domain\Repository\UserRepository;
use App\Administration\Infrastructure\Security\SecurityUser;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use Doctrine\DBAL\Connection;
use PHPUnit\Framework\Attributes\Test;

/**
 * Tests for image rights API endpoints.
 *
 * @see Story 2.12 - Consultation Liste Droit à l'Image
 */
final class ImageRightsEndpointsTest extends ApiTestCase
{
    protected static ?bool $alwaysBootKernel = true;

    private const string TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
    private const string USER_ID = '550e8400-e29b-41d4-a716-446655440000';
    private const string STUDENT_1_ID = 'aaaaaaaa-bbbb-cccc-dddd-111111111111';
    private const string STUDENT_2_ID = 'aaaaaaaa-bbbb-cccc-dddd-222222222222';
    private const string BASE_URL = 'http://ecole-alpha.classeo.local/api';

    protected function tearDown(): void
    {
        $container = static::getContainer();
        /** @var Connection $connection */
        $connection = $container->get(Connection::class);
        $connection->executeStatement('UPDATE users SET image_rights_updated_by = NULL WHERE image_rights_updated_by = :id', ['id' => self::USER_ID]);
        $connection->executeStatement('DELETE FROM users WHERE id = :id', ['id' => self::STUDENT_1_ID]);
        $connection->executeStatement('DELETE FROM users WHERE id = :id', ['id' => self::STUDENT_2_ID]);
        $connection->executeStatement('DELETE FROM users WHERE id = :id', ['id' => self::USER_ID]);

        /** @var PaginatedQueryCache $paginatedCache */
        $paginatedCache = $container->get(PaginatedQueryCache::class);
        $paginatedCache->invalidate('students_image_rights', self::TENANT_ID);

        parent::tearDown();
    }

    // =========================================================================
    // Security - Without tenant
    // =========================================================================

    #[Test]
    public function getImageRightsReturns404WithoutTenant(): void
    {
        $client = static::createClient();

        $client->request('GET', '/api/students/image-rights', [
            'headers' => [
                'Host' => 'localhost',
                'Accept' => 'application/json',
            ],
        ]);

        self::assertResponseStatusCodeSame(404);
    }

    #[Test]
    public function updateImageRightsReturns404WithoutTenant(): void
    {
        $client = static::createClient();

        $client->request('PATCH', '/api/students/' . self::STUDENT_1_ID . '/image-rights', [
            'headers' => [
                'Host' => 'localhost',
                'Accept' => 'application/json',
                'Content-Type' => 'application/merge-patch+json',
            ],
            'json' => ['imageRightsStatus' => 'authorized'],
        ]);

        self::assertResponseStatusCodeSame(404);
    }

    #[Test]
    public function exportImageRightsReturns404WithoutTenant(): void
    {
        $client = static::createClient();

        $client->request('GET', '/api/students/image-rights/export', [
            'headers' => [
                'Host' => 'localhost',
                'Accept' => 'application/json',
            ],
        ]);

        self::assertResponseStatusCodeSame(404);
    }

    // =========================================================================
    // Security - Without authentication
    // =========================================================================

    #[Test]
    public function getImageRightsReturns401WithoutAuthentication(): void
    {
        $client = static::createClient();

        $client->request('GET', self::BASE_URL . '/students/image-rights', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseStatusCodeSame(401);
    }

    #[Test]
    public function updateImageRightsReturns401WithoutAuthentication(): void
    {
        $client = static::createClient();

        $client->request('PATCH', self::BASE_URL . '/students/' . self::STUDENT_1_ID . '/image-rights', [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/merge-patch+json',
            ],
            'json' => ['imageRightsStatus' => 'authorized'],
        ]);

        self::assertResponseStatusCodeSame(401);
    }

    #[Test]
    public function exportImageRightsReturns401WithoutAuthentication(): void
    {
        $client = static::createClient();

        $client->request('GET', self::BASE_URL . '/students/image-rights/export', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseStatusCodeSame(401);
    }

    // =========================================================================
    // Security - Forbidden roles
    // =========================================================================

    #[Test]
    public function getImageRightsReturns403ForStudent(): void
    {
        $client = $this->createAuthenticatedClient(['ROLE_ELEVE']);

        $client->request('GET', self::BASE_URL . '/students/image-rights', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseStatusCodeSame(403);
    }

    #[Test]
    public function updateImageRightsReturns403ForStudent(): void
    {
        $client = $this->createAuthenticatedClient(['ROLE_ELEVE']);

        $client->request('PATCH', self::BASE_URL . '/students/' . self::STUDENT_1_ID . '/image-rights', [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/merge-patch+json',
            ],
            'json' => ['imageRightsStatus' => 'authorized'],
        ]);

        self::assertResponseStatusCodeSame(403);
    }

    #[Test]
    public function exportImageRightsReturns403ForStudent(): void
    {
        $client = $this->createAuthenticatedClient(['ROLE_ELEVE']);

        $client->request('GET', self::BASE_URL . '/students/image-rights/export', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseStatusCodeSame(403);
    }

    #[Test]
    public function getImageRightsReturns200ForTeacher(): void
    {
        $client = $this->createAuthenticatedClient(['ROLE_PROF']);

        $client->request('GET', self::BASE_URL . '/students/image-rights', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseStatusCodeSame(200);
    }

    #[Test]
    public function getImageRightsReturns403ForParent(): void
    {
        $client = $this->createAuthenticatedClient(['ROLE_PARENT']);

        $client->request('GET', self::BASE_URL . '/students/image-rights', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseStatusCodeSame(403);
    }

    // =========================================================================
    // AC1 (P1) - GET image rights collection
    // =========================================================================

    #[Test]
    public function getImageRightsReturnsStudentsForAdmin(): void
    {
        $this->persistStudent(self::STUDENT_1_ID, 'Alice', 'Dupont', 'alice-ir@test.classeo.local', ImageRightsStatus::AUTHORIZED);
        $this->persistStudent(self::STUDENT_2_ID, 'Bob', 'Martin', 'bob-ir@test.classeo.local', ImageRightsStatus::REFUSED);

        $client = $this->createAuthenticatedClient(['ROLE_ADMIN']);
        $response = $client->request('GET', self::BASE_URL . '/students/image-rights', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseIsSuccessful();
        $data = $response->toArray();
        self::assertNotEmpty($data);
        self::assertArrayHasKey('id', $data[0]);
        self::assertArrayHasKey('imageRightsStatus', $data[0]);
    }

    #[Test]
    public function getImageRightsReturnsStudentsForSecretariat(): void
    {
        $this->persistStudent(self::STUDENT_1_ID, 'Alice', 'Dupont', 'alice-ir@test.classeo.local', ImageRightsStatus::AUTHORIZED);

        $client = $this->createAuthenticatedClient(['ROLE_SECRETARIAT']);
        $response = $client->request('GET', self::BASE_URL . '/students/image-rights', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseIsSuccessful();
        $data = $response->toArray();
        self::assertNotEmpty($data);
        self::assertArrayHasKey('id', $data[0]);
        self::assertArrayHasKey('imageRightsStatus', $data[0]);
    }

    // =========================================================================
    // AC2 (P1) - Filter by status
    // =========================================================================

    #[Test]
    public function getImageRightsFiltersByStatus(): void
    {
        $this->persistStudent(self::STUDENT_1_ID, 'Alice', 'Dupont', 'alice-ir@test.classeo.local', ImageRightsStatus::AUTHORIZED);
        $this->persistStudent(self::STUDENT_2_ID, 'Bob', 'Martin', 'bob-ir@test.classeo.local', ImageRightsStatus::REFUSED);

        $client = $this->createAuthenticatedClient(['ROLE_ADMIN']);
        $response = $client->request('GET', self::BASE_URL . '/students/image-rights?status=authorized', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseIsSuccessful();
        $data = $response->toArray();
        self::assertNotEmpty($data);

        foreach ($data as $member) {
            self::assertSame('authorized', $member['imageRightsStatus']);
        }
    }

    #[Test]
    public function getImageRightsFiltersByStatusNotSpecified(): void
    {
        $this->persistStudent(self::STUDENT_1_ID, 'Alice', 'Dupont', 'alice-ir@test.classeo.local', ImageRightsStatus::NOT_SPECIFIED);
        $this->persistStudent(self::STUDENT_2_ID, 'Bob', 'Martin', 'bob-ir@test.classeo.local', ImageRightsStatus::AUTHORIZED);

        $client = $this->createAuthenticatedClient(['ROLE_ADMIN']);
        $response = $client->request('GET', self::BASE_URL . '/students/image-rights?status=not_specified', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseIsSuccessful();
        $data = $response->toArray();
        self::assertNotEmpty($data);

        foreach ($data as $member) {
            self::assertSame('not_specified', $member['imageRightsStatus']);
        }
    }

    // =========================================================================
    // AC3 (P0) - PATCH update image rights
    // =========================================================================

    #[Test]
    public function updateImageRightsReturns200ForAdmin(): void
    {
        $this->persistAdmin();
        $this->persistStudent(self::STUDENT_1_ID, 'Alice', 'Dupont', 'alice-ir@test.classeo.local', ImageRightsStatus::NOT_SPECIFIED);

        $client = $this->createAuthenticatedClient(['ROLE_ADMIN']);
        $response = $client->request('PATCH', self::BASE_URL . '/students/' . self::STUDENT_1_ID . '/image-rights', [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/merge-patch+json',
            ],
            'json' => ['imageRightsStatus' => 'authorized'],
        ]);

        self::assertResponseIsSuccessful();
        $data = $response->toArray();
        self::assertSame('authorized', $data['imageRightsStatus']);
        self::assertSame(self::STUDENT_1_ID, $data['id']);
    }

    #[Test]
    public function updateImageRightsReturns404ForUnknownStudent(): void
    {
        $client = $this->createAuthenticatedClient(['ROLE_ADMIN']);

        $client->request('PATCH', self::BASE_URL . '/students/00000000-0000-0000-0000-000000000000/image-rights', [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/merge-patch+json',
            ],
            'json' => ['imageRightsStatus' => 'authorized'],
        ]);

        self::assertResponseStatusCodeSame(404);
    }

    #[Test]
    public function updateImageRightsReturns422ForInvalidStatus(): void
    {
        $this->persistStudent(self::STUDENT_1_ID, 'Alice', 'Dupont', 'alice-ir@test.classeo.local', ImageRightsStatus::NOT_SPECIFIED);

        $client = $this->createAuthenticatedClient(['ROLE_ADMIN']);

        $client->request('PATCH', self::BASE_URL . '/students/' . self::STUDENT_1_ID . '/image-rights', [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/merge-patch+json',
            ],
            'json' => ['imageRightsStatus' => 'invalid_status'],
        ]);

        self::assertResponseStatusCodeSame(422);
    }

    // =========================================================================
    // AC4 (P1) - Export CSV
    // =========================================================================

    #[Test]
    public function exportImageRightsReturnsCsvForAdmin(): void
    {
        $this->persistStudent(self::STUDENT_1_ID, 'Alice', 'Dupont', 'alice-ir@test.classeo.local', ImageRightsStatus::AUTHORIZED);

        $client = $this->createAuthenticatedClient(['ROLE_ADMIN']);
        $response = $client->request('GET', self::BASE_URL . '/students/image-rights/export', [
            'headers' => ['Accept' => 'application/json'],
        ]);

        self::assertResponseIsSuccessful();
        self::assertResponseHeaderSame('content-type', 'text/csv; charset=UTF-8');
        self::assertResponseHeaderSame('content-disposition', 'attachment; filename="droits-image.csv"');

        $content = $response->getContent();
        self::assertStringContainsString('Nom', $content);
        self::assertStringContainsString('Dupont', $content);
    }

    // =========================================================================
    // Helpers
    // =========================================================================

    private function createAuthenticatedClient(array $roles): \ApiPlatform\Symfony\Bundle\Test\Client
    {
        $client = static::createClient();

        $user = new SecurityUser(
            userId: UserId::fromString(self::USER_ID),
            email: 'admin@classeo.local',
            hashedPassword: '',
            tenantId: TenantId::fromString(self::TENANT_ID),
            roles: $roles,
        );

        $client->loginUser($user, 'api');

        return $client;
    }

    private function persistAdmin(): void
    {
        $admin = User::reconstitute(
            id: UserId::fromString(self::USER_ID),
            email: new Email('admin@classeo.local'),
            roles: [Role::ADMIN],
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: 'École Alpha',
            statut: StatutCompte::ACTIF,
            dateNaissance: null,
            createdAt: new DateTimeImmutable('2026-01-01'),
            hashedPassword: 'dummy',
            activatedAt: new DateTimeImmutable('2026-01-01'),
            consentementParental: null,
        );

        /** @var UserRepository $repository */
        $repository = static::getContainer()->get(UserRepository::class);
        $repository->save($admin);
    }

    private function persistStudent(
        string $id,
        string $firstName,
        string $lastName,
        string $email,
        ImageRightsStatus $imageRightsStatus,
    ): void {
        $user = User::reconstitute(
            id: UserId::fromString($id),
            email: new Email($email),
            roles: [Role::ELEVE],
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: 'École Alpha',
            statut: StatutCompte::EN_ATTENTE,
            dateNaissance: new DateTimeImmutable('2012-06-15'),
            createdAt: new DateTimeImmutable('2026-01-15'),
            hashedPassword: null,
            activatedAt: null,
            consentementParental: null,
            firstName: $firstName,
            lastName: $lastName,
            imageRightsStatus: $imageRightsStatus,
        );

        /** @var UserRepository $repository */
        $repository = static::getContainer()->get(UserRepository::class);
        $repository->save($user);
    }
}
