<?php

declare(strict_types=1);

namespace App\Tests\Unit\Shared\Infrastructure\Monitoring;

use App\Shared\Infrastructure\Monitoring\HealthCheckController;
use App\Shared\Infrastructure\Monitoring\InfrastructureHealthCheckerInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Response;

/**
 * Stub for InfrastructureHealthCheckerInterface.
 */
final class InfrastructureHealthCheckerStub implements InfrastructureHealthCheckerInterface
{
    /**
     * @param array{postgres: bool, redis: bool, rabbitmq: bool} $checks
     */
    public function __construct(
        private readonly array $checks = ['postgres' => true, 'redis' => true, 'rabbitmq' => true],
    ) {
    }

    public function checkPostgres(): bool
    {
        return $this->checks['postgres'];
    }

    public function checkRedis(): bool
    {
        return $this->checks['redis'];
    }

    public function checkRabbitMQ(): bool
    {
        return $this->checks['rabbitmq'];
    }

    public function checkAll(): array
    {
        return $this->checks;
    }
}

/**
 * @see Story 1.8 - T7: Health Check Endpoint
 */
#[CoversClass(HealthCheckController::class)]
final class HealthCheckControllerTest extends TestCase
{
    private function createController(
        ?InfrastructureHealthCheckerInterface $healthChecker = null,
    ): HealthCheckController {
        $healthChecker ??= new InfrastructureHealthCheckerStub();

        return new HealthCheckController($healthChecker);
    }

    #[Test]
    public function itReturnsHealthyWhenAllServicesAreUp(): void
    {
        $controller = $this->createController();

        $response = $controller();

        self::assertSame(Response::HTTP_OK, $response->getStatusCode());

        $data = json_decode($response->getContent(), true);
        self::assertSame('healthy', $data['status']);
        self::assertTrue($data['checks']['postgres']);
        self::assertTrue($data['checks']['redis']);
        self::assertTrue($data['checks']['rabbitmq']);
    }

    #[Test]
    public function itReturnsUnhealthyWhenPostgresIsDown(): void
    {
        $checker = new InfrastructureHealthCheckerStub([
            'postgres' => false,
            'redis' => true,
            'rabbitmq' => true,
        ]);
        $controller = $this->createController($checker);

        $response = $controller();

        self::assertSame(Response::HTTP_SERVICE_UNAVAILABLE, $response->getStatusCode());

        $data = json_decode($response->getContent(), true);
        self::assertSame('unhealthy', $data['status']);
        self::assertFalse($data['checks']['postgres']);
    }

    #[Test]
    public function itIncludesTimestampInResponse(): void
    {
        $controller = $this->createController();

        $response = $controller();
        $data = json_decode($response->getContent(), true);

        self::assertArrayHasKey('timestamp', $data);
        self::assertMatchesRegularExpression(
            '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{2}:\d{2}$/',
            $data['timestamp'],
        );
    }

    #[Test]
    public function itReturnsAllServiceChecks(): void
    {
        $controller = $this->createController();

        $response = $controller();
        $data = json_decode($response->getContent(), true);

        self::assertArrayHasKey('postgres', $data['checks']);
        self::assertArrayHasKey('redis', $data['checks']);
        self::assertArrayHasKey('rabbitmq', $data['checks']);
    }

    #[Test]
    public function itReturnsUnhealthyWhenRabbitmqIsDown(): void
    {
        $checker = new InfrastructureHealthCheckerStub([
            'postgres' => true,
            'redis' => true,
            'rabbitmq' => false,
        ]);
        $controller = $this->createController($checker);

        $response = $controller();

        self::assertSame(Response::HTTP_SERVICE_UNAVAILABLE, $response->getStatusCode());

        $data = json_decode($response->getContent(), true);
        self::assertFalse($data['checks']['rabbitmq']);
    }

    #[Test]
    public function itReturnsUnhealthyWhenRedisIsDown(): void
    {
        $checker = new InfrastructureHealthCheckerStub([
            'postgres' => true,
            'redis' => false,
            'rabbitmq' => true,
        ]);
        $controller = $this->createController($checker);

        $response = $controller();

        self::assertSame(Response::HTTP_SERVICE_UNAVAILABLE, $response->getStatusCode());

        $data = json_decode($response->getContent(), true);
        self::assertFalse($data['checks']['redis']);
    }
}
