<?php

namespace App\Tests\Unit\Service;

use App\Service\Forecast\DriftSimulationService;
use App\Service\Ocean\OceanCurrentClient;
use App\Service\Weather\OpenMeteoClient;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;

class DriftSimulationServiceTest extends TestCase
{
    private DriftSimulationService $service;

    protected function setUp(): void
    {
        $this->service = new DriftSimulationService(
            $this->createStub(OpenMeteoClient::class),
            $this->createStub(OceanCurrentClient::class),
            $this->createStub(Connection::class),
            $this->createStub(EntityManagerInterface::class),
            new NullLogger(),
        );
    }

    // ── computeConfidence ─────────────────────────────────────────────────────

    #[\PHPUnit\Framework\Attributes\DataProvider('confidenceProvider')]
    public function testComputeConfidence(int $horizon, float $expected): void
    {
        $method = new \ReflectionMethod(DriftSimulationService::class, 'computeConfidence');
        $result = $method->invoke($this->service, $horizon);

        self::assertSame($expected, $result);
    }

    /** @return array<string, array{int, float}> */
    public static function confidenceProvider(): array
    {
        return [
            'H+6  → 0.85' => [6,   0.85],
            'H+12 → 0.75' => [12,  0.75],
            'H+24 → 0.60' => [24,  0.60],
            'H+48 → 0.45' => [48,  0.45],
            'inconnu → 0.50' => [72, 0.50],
        ];
    }

    // ── avgDriftVector ────────────────────────────────────────────────────────

    public function testAvgDriftVectorWithNoWindNoOcean(): void
    {
        $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector');
        $result = $method->invoke($this->service, [], 0, 6, 0.0, 0.0);

        self::assertSame(0.0, $result['lat']);
        self::assertSame(0.0, $result['lng']);
    }

    public function testAvgDriftVectorWithPureEastwardOceanCurrent(): void
    {
        // Courant purement Est (u=1.0 m/s), vent nul, 6 heures
        $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector');
        $result = $method->invoke($this->service, [], 0, 6, 1.0, 0.0);

        self::assertSame(1.0, $result['lng']); // Est-Ouest = 1 m/s
        self::assertSame(0.0, $result['lat']); // Nord-Sud = 0
    }

    public function testAvgDriftVectorWithNorthwardOceanCurrent(): void
    {
        $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector');
        $result = $method->invoke($this->service, [], 0, 6, 0.0, 1.0);

        self::assertSame(0.0, $result['lng']);
        self::assertSame(1.0, $result['lat']);
    }

    public function testAvgDriftVectorWithNorthWind(): void
    {
        // Vent venant du Nord (direction=0°), vitesse=10 m/s, courant nul
        // sin(0°)=0 (pas de composante E-O), cos(0°)=1 (composante N-S)
        // Stokes = 3% → 10 * 0.03 * cos(0) = 0.3 m/s vers Nord
        $windData = array_fill(0, 6, ['speed' => 10.0, 'direction' => 0.0]);
        $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector');
        $result = $method->invoke($this->service, $windData, 0, 6, 0.0, 0.0);

        self::assertEqualsWithDelta(0.0, $result['lng'], 1e-10);
        self::assertEqualsWithDelta(0.3, $result['lat'], 1e-10);
    }

    public function testAvgDriftVectorWithEastWind(): void
    {
        // Vent venant de l'Est (direction=90°), vitesse=10 m/s
        // sin(90°)=1 → composante Est = 10*0.03 = 0.3 m/s
        // cos(90°)=0 → composante Nord = 0
        $windData = array_fill(0, 6, ['speed' => 10.0, 'direction' => 90.0]);
        $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector');
        $result = $method->invoke($this->service, $windData, 0, 6, 0.0, 0.0);

        self::assertEqualsWithDelta(0.3, $result['lng'], 1e-10);
        self::assertEqualsWithDelta(0.0, $result['lat'], 1e-10);
    }

    public function testAvgDriftVectorUsesOnlyRequestedHourRange(): void
    {
        // windData[0..5] = vent Nord 10 m/s, windData[6..11] = vent Est 10 m/s
        // Si on demande fromHour=6, toHour=12 → seules les heures 6..11 comptent
        $windData = array_merge(
            array_fill(0, 6, ['speed' => 10.0, 'direction' => 0.0]),  // heures 0-5 : Nord
            array_fill(6, 6, ['speed' => 10.0, 'direction' => 90.0]), // heures 6-11 : Est
        );
        $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector');
        $result = $method->invoke($this->service, $windData, 6, 12, 0.0, 0.0);

        self::assertEqualsWithDelta(0.3, $result['lng'], 1e-10); // Est
        self::assertEqualsWithDelta(0.0, $result['lat'], 1e-10);
    }
}
