<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use App\Facades\TaxonomyMapper;
use App\VehicleSpec\Services\CapSectorBodyTypeService;
use Database\Seeders\Tenant\VehicleSpecSeeder;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Http;
use Mockery;
use Mtc\MercuryDataModels\BodyStyleType;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TenantTestCase;

class CapSectorBodyTypeServiceTest extends TenantTestCase
{
    private const SECTOR_ENDPOINT = 'https://soap.cap.co.uk/vehicles/capvehicles.asmx/GetCapDescriptionFromIdWithSector';

    protected function setUp(): void
    {
        parent::setUp();
        $this->seed(VehicleSpecSeeder::class);

        Settings::update('vehicle-spec-providers-cap-enabled', true);
        Settings::update('vehicle-spec-providers-cap-subscriber_id', 12345);
        Settings::update('vehicle-spec-providers-cap-password', 'test_password');
    }

    public function testReturnsFalseWhenSettingIsOff(): void
    {
        Settings::update('vehicle-spec-providers-cap-use-sector-for-body-type', false);

        $vehicle = Vehicle::factory()->create(['cap_id' => '101666']);

        Http::fake(['*' => Http::response('should-not-be-called', 500)]);

        $result = App::make(CapSectorBodyTypeService::class)->applyForVehicle($vehicle);

        $this->assertFalse($result);
        Http::assertNothingSent();
    }

    public function testReturnsFalseWhenVehicleHasNoCapId(): void
    {
        Settings::update('vehicle-spec-providers-cap-use-sector-for-body-type', true);

        $vehicle = Vehicle::factory()->create(['cap_id' => null]);

        $result = App::make(CapSectorBodyTypeService::class)->applyForVehicle($vehicle);

        $this->assertFalse($result);
    }

    public function testUpdatesBodyStyleWhenFirstCandidateMapsToKnownBodyStyle(): void
    {
        Settings::update('vehicle-spec-providers-cap-use-sector-for-body-type', true);

        $convertible = BodyStyleType::factory()->create(['name' => 'Convertible']);
        $coupe = BodyStyleType::factory()->create(['name' => 'Coupe']);

        $vehicle = Vehicle::factory()->create([
            'cap_id' => '101666',
            'body_style_id' => $coupe->id,
        ]);

        Http::fake([
            self::SECTOR_ENDPOINT => $this->sectorResponse('Convertible Small'),
            '*' => Http::response('unexpected', 404),
        ]);

        $this->stubTaxonomyMapperReturns(['Convertible' => $convertible->id]);

        $result = App::make(CapSectorBodyTypeService::class)->applyForVehicle($vehicle);

        $this->assertTrue($result);
        $vehicle->refresh();
        $this->assertEquals($convertible->id, $vehicle->body_style_id);
    }

    public function testPrestigePrefixedSectorFallsThroughToBodyTypeCandidate(): void
    {
        Settings::update('vehicle-spec-providers-cap-use-sector-for-body-type', true);

        $convertible = BodyStyleType::factory()->create(['name' => 'Convertible']);

        $vehicle = Vehicle::factory()->create([
            'cap_id' => '999',
            'body_style_id' => null,
        ]);

        Http::fake([
            self::SECTOR_ENDPOINT => $this->sectorResponse('Prestige Convertible Small'),
            '*' => Http::response('unexpected', 404),
        ]);

        // 'Prestige' returns null (not a body type), 'Convertible' returns the id, 'Small' shouldn't be reached.
        $this->stubTaxonomyMapperReturns(['Convertible' => $convertible->id]);

        $result = App::make(CapSectorBodyTypeService::class)->applyForVehicle($vehicle);

        $this->assertTrue($result);
        $vehicle->refresh();
        $this->assertEquals($convertible->id, $vehicle->body_style_id);
    }

    public function testLeavesBodyStyleUntouchedWhenNoCandidatesMap(): void
    {
        Settings::update('vehicle-spec-providers-cap-use-sector-for-body-type', true);

        $coupe = BodyStyleType::factory()->create(['name' => 'Coupe']);

        $vehicle = Vehicle::factory()->create([
            'cap_id' => '2',
            'body_style_id' => $coupe->id,
        ]);

        Http::fake([
            self::SECTOR_ENDPOINT => $this->sectorResponse('Supermini'),
            '*' => Http::response('unexpected', 404),
        ]);

        // Default UsesTenancy mock already returns null for all - no override needed.

        $result = App::make(CapSectorBodyTypeService::class)->applyForVehicle($vehicle);

        $this->assertFalse($result);
        $vehicle->refresh();
        $this->assertEquals($coupe->id, $vehicle->body_style_id);
    }

    public function testReturnsFalseWhenMappedBodyStyleAlreadyMatches(): void
    {
        Settings::update('vehicle-spec-providers-cap-use-sector-for-body-type', true);

        $convertible = BodyStyleType::factory()->create(['name' => 'Convertible']);

        $vehicle = Vehicle::factory()->create([
            'cap_id' => '101666',
            'body_style_id' => $convertible->id,
        ]);

        Http::fake([
            self::SECTOR_ENDPOINT => $this->sectorResponse('Convertible Small'),
            '*' => Http::response('unexpected', 404),
        ]);

        $this->stubTaxonomyMapperReturns(['Convertible' => $convertible->id]);

        $result = App::make(CapSectorBodyTypeService::class)->applyForVehicle($vehicle);

        $this->assertFalse($result);
        $vehicle->refresh();
        $this->assertEquals($convertible->id, $vehicle->body_style_id);
    }

    /**
     * The base TenantTestCase mocks TaxonomyMapper globally to return null for
     * every lookup. Swap the resolved facade with a fresh Mockery mock that
     * returns caller-controlled body_style ids for specific candidate terms.
     */
    private function stubTaxonomyMapperReturns(array $termToId): void
    {
        $mock = Mockery::mock();
        $mock->shouldReceive('getMappedTaxonomy')
            ->andReturnUsing(function ($provider, $taxonomy, $term) use ($termToId) {
                return $termToId[$term] ?? null;
            });
        $mock->shouldReceive('storeUnmappedTaxonomies')->andReturn(false);
        TaxonomyMapper::swap($mock);
    }

    private function sectorResponse(string $sectorDescription)
    {
        $xml = file_get_contents(base_path('tests/data/cap/GetCapDescriptionFromIdWithSector.xml'));
        $xml = str_replace('{SECTOR_DESCRIPTION}', $sectorDescription, $xml);
        return Http::response($xml, 200);
    }
}
