<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Infrastructure\Api\Provider;

use ApiPlatform\Metadata\GetCollection;
use App\Administration\Application\Query\GetStudentsForParent\GetStudentsForParentHandler;
use App\Administration\Domain\Model\StudentGuardian\RelationshipType;
use App\Administration\Domain\Model\StudentGuardian\StudentGuardian;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\User;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Domain\Repository\UserRepository;
use App\Administration\Infrastructure\Api\Provider\MyChildrenProvider;
use App\Administration\Infrastructure\Api\Resource\MyChildrenResource;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryStudentGuardianRepository;
use App\Administration\Infrastructure\Security\SecurityUser;
use App\Shared\Domain\Tenant\TenantId;
use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantContext;
use App\Shared\Infrastructure\Tenant\TenantId as InfraTenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Symfony\Component\Security\Core\User\UserInterface;

final class MyChildrenProviderTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string SUBDOMAIN = 'ecole-alpha';
    private const string PARENT_ID = '550e8400-e29b-41d4-a716-446655440020';
    private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440010';

    private InMemoryStudentGuardianRepository $repository;
    private TenantContext $tenantContext;
    private SecurityUser $securityUser;

    protected function setUp(): void
    {
        $this->repository = new InMemoryStudentGuardianRepository();

        $this->tenantContext = new TenantContext();
        $this->tenantContext->setCurrentTenant(new TenantConfig(
            tenantId: InfraTenantId::fromString(self::TENANT_ID),
            subdomain: self::SUBDOMAIN,
            databaseUrl: 'postgresql://test',
        ));

        $this->securityUser = new SecurityUser(
            userId: UserId::fromString(self::PARENT_ID),
            email: 'parent@example.com',
            hashedPassword: '$argon2id$hashed',
            tenantId: TenantId::fromString(self::TENANT_ID),
            roles: [Role::PARENT->value],
        );
    }

    #[Test]
    public function returnsChildrenForAuthenticatedParent(): void
    {
        $link = StudentGuardian::lier(
            studentId: UserId::fromString(self::STUDENT_ID),
            guardianId: UserId::fromString(self::PARENT_ID),
            relationshipType: RelationshipType::FATHER,
            tenantId: TenantId::fromString(self::TENANT_ID),
            createdAt: new DateTimeImmutable('2026-02-10 10:00:00'),
        );
        $this->repository->save($link);

        $provider = $this->createProvider();

        $results = $provider->provide(new GetCollection());

        self::assertCount(1, $results);
        self::assertInstanceOf(MyChildrenResource::class, $results[0]);
        self::assertSame((string) $link->id, $results[0]->id);
        self::assertSame(self::STUDENT_ID, $results[0]->studentId);
        self::assertSame('père', $results[0]->relationshipType);
        self::assertSame('Père', $results[0]->relationshipLabel);
    }

    #[Test]
    public function returnsEmptyArrayWhenNoChildren(): void
    {
        $provider = $this->createProvider();

        $results = $provider->provide(new GetCollection());

        self::assertSame([], $results);
    }

    #[Test]
    public function throwsUnauthorizedWhenNotAuthenticated(): void
    {
        $security = $this->createMock(Security::class);
        $security->method('getUser')->willReturn(null);

        $provider = $this->createProvider(security: $security);

        $this->expectException(UnauthorizedHttpException::class);

        $provider->provide(new GetCollection());
    }

    #[Test]
    public function throwsUnauthorizedWhenNoTenant(): void
    {
        $tenantContext = new TenantContext();

        $provider = $this->createProvider(tenantContext: $tenantContext);

        $this->expectException(UnauthorizedHttpException::class);

        $provider->provide(new GetCollection());
    }

    #[Test]
    public function throwsUnauthorizedWhenNotSecurityUser(): void
    {
        $nonSecurityUser = $this->createMock(UserInterface::class);

        $security = $this->createMock(Security::class);
        $security->method('getUser')->willReturn($nonSecurityUser);

        $provider = $this->createProvider(security: $security);

        $this->expectException(UnauthorizedHttpException::class);

        $provider->provide(new GetCollection());
    }

    private function createProvider(
        ?TenantContext $tenantContext = null,
        ?Security $security = null,
    ): MyChildrenProvider {
        $studentUser = User::creer(
            email: new Email('student@example.com'),
            role: Role::ELEVE,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: 'École Test',
            dateNaissance: null,
            createdAt: new DateTimeImmutable('2026-02-10 10:00:00'),
        );

        $userRepository = $this->createMock(UserRepository::class);
        $userRepository->method('get')->willReturn($studentUser);

        $handler = new GetStudentsForParentHandler($this->repository, $userRepository);

        $tenantContext ??= $this->tenantContext;

        if ($security === null) {
            $security = $this->createMock(Security::class);
            $security->method('getUser')->willReturn($this->securityUser);
        }

        return new MyChildrenProvider(
            $handler,
            $security,
            $tenantContext,
        );
    }
}
