<?php

namespace Tests\Feature;

use App\Facades\Feature;
use App\Services\Geolocation\Contracts\GeolocationResolver;
use Illuminate\Database\Eloquent\Model;
use Mtc\MercuryDataModels\Country;
use Mtc\MercuryDataModels\Currency;
use Tests\DatabaseTestCase;

class CurrencyDetectionTest extends DatabaseTestCase
{
    #[\Override]
    protected function setUp(): void
    {
        parent::setUp();
        Country::unguard();
        Currency::unguard();
    }

    #[\Override]
    protected function tearDown(): void
    {
        // unguard() is global to all models; reguard so the relaxed state does not
        // leak into other tests sharing the same (paratest) process.
        Model::reguard();
        parent::tearDown();
    }

    public function testGetSiteExposesDetectedCurrencyWhenMultiCurrencyEnabled(): void
    {
        Feature::setEnabled(tenant(), ['multi-currency']);
        Currency::query()->create(['code' => 'GBP', 'ratio' => 1.0]);
        Country::query()->create(['code' => 'GB', 'name' => 'United Kingdom', 'currency_code' => 'GBP']);

        $this->fakeGeolocation(['89.107.62.116' => 'GB']);

        $response = $this->asTenant(tenant())
            ->withHeader('X-Forwarded-For', '89.107.62.116, 172.31.23.149')
            ->getJson(route('get-site'));

        $response->assertStatus(200);
        $this->assertSame('GBP', $response->json('detected_currency'));
    }

    public function testDetectedCurrencyIsNullWhenMultiCurrencyDisabled(): void
    {
        Currency::query()->create(['code' => 'GBP', 'ratio' => 1.0]);
        Country::query()->create(['code' => 'GB', 'name' => 'United Kingdom', 'currency_code' => 'GBP']);

        $this->fakeGeolocation(['89.107.62.116' => 'GB']);

        $response = $this->asTenant(tenant())
            ->withHeader('X-Forwarded-For', '89.107.62.116')
            ->getJson(route('get-site'));

        $response->assertStatus(200);
        $this->assertNull($response->json('detected_currency'));
    }

    public function testDetectedCurrencyIsNullWhenCountryHasNoSupportedCurrency(): void
    {
        Feature::setEnabled(tenant(), ['multi-currency']);
        Country::query()->create(['code' => 'XX', 'name' => 'Nowhere', 'currency_code' => 'XYZ']);

        $this->fakeGeolocation(['89.107.62.116' => 'XX']);

        $response = $this->asTenant(tenant())
            ->withHeader('X-Forwarded-For', '89.107.62.116')
            ->getJson(route('get-site'));

        $response->assertStatus(200);
        $this->assertNull($response->json('detected_currency'));
    }

    public function testDetectedCurrencyIsNullWhenGeolocationFails(): void
    {
        Feature::setEnabled(tenant(), ['multi-currency']);
        Currency::query()->create(['code' => 'GBP', 'ratio' => 1.0]);
        Country::query()->create(['code' => 'GB', 'name' => 'United Kingdom', 'currency_code' => 'GBP']);

        $this->fakeGeolocation([]);

        $response = $this->asTenant(tenant())
            ->withHeader('X-Forwarded-For', '89.107.62.116')
            ->getJson(route('get-site'));

        $response->assertStatus(200);
        $this->assertNull($response->json('detected_currency'));
    }

    private function fakeGeolocation(array $map): void
    {
        $this->app->instance(GeolocationResolver::class, new class ($map) implements GeolocationResolver {
            public function __construct(private readonly array $map)
            {
            }

            public function countryCodeForIp(string $ip): ?string
            {
                return $this->map[$ip] ?? null;
            }
        });
    }
}
