<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use App\Modules\Stock\Modix;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleFeature;
use SimpleXMLElement;
use Tests\TenantTestCase;

class ModixTest extends TenantTestCase
{
    public function testDates()
    {
        $xml = '<vehicles>
                    <vehicle>
                        <dates>
                            <date id="2">2022-06-24</date>
                            <date id="1">2020-02-06</date>
                            <date id="7">2024-03-07</date>
                        </dates>
                    </vehicle>
                </vehicles>';

        $xml_element = new SimpleXMLElement($xml);
        $vehicles = $xml_element->xpath('//vehicles/vehicle');
        $vehicle = reset($vehicles);

        $modix = new Modix();
        $methodGetManufactureDate = new \ReflectionMethod(Modix::class, 'getManufactureDate');
        $methodGetFirstRegistrationDate = new \ReflectionMethod(Modix::class, 'getFirstRegistrationDate');
        $manufacture_date = $methodGetManufactureDate->invoke($modix, $vehicle);
        $registration_date = $methodGetFirstRegistrationDate->invoke($modix, $vehicle);

        $this->assertEquals('2020-02-06', $manufacture_date);
        $this->assertEquals('2022-06-24', $registration_date);
    }

    public function testDatesEmpty()
    {
        $xml = '<vehicles>
                    <vehicle>
                        <dates>
                        </dates>
                    </vehicle>
                </vehicles>';

        $xml_element = new SimpleXMLElement($xml);
        $vehicles = $xml_element->xpath('//vehicles/vehicle');
        $vehicle = reset($vehicles);

        $modix = new Modix();
        $methodGetManufactureDate = new \ReflectionMethod(Modix::class, 'getManufactureDate');
        $methodGetFirstRegistrationDate = new \ReflectionMethod(Modix::class, 'getFirstRegistrationDate');
        $manufacture_date = $methodGetManufactureDate->invoke($modix, $vehicle);
        $registration_date = $methodGetFirstRegistrationDate->invoke($modix, $vehicle);

        $this->assertEmpty($manufacture_date);
        $this->assertEmpty($registration_date);
    }

    public function testDatesMissing()
    {
        $xml = '<vehicles>
                    <vehicle>
                    </vehicle>
                </vehicles>';

        $xml_element = new SimpleXMLElement($xml);
        $vehicles = $xml_element->xpath('//vehicles/vehicle');
        $vehicle = reset($vehicles);

        $modix = new Modix();
        $methodGetManufactureDate = new \ReflectionMethod(Modix::class, 'getManufactureDate');
        $methodGetFirstRegistrationDate = new \ReflectionMethod(Modix::class, 'getFirstRegistrationDate');
        $manufacture_date = $methodGetManufactureDate->invoke($modix, $vehicle);
        $registration_date = $methodGetFirstRegistrationDate->invoke($modix, $vehicle);

        $this->assertEmpty($manufacture_date);
        $this->assertEmpty($registration_date);
    }

    public function testEvData()
    {
        $xml = '<vehicles>
                    <vehicle>
                        <colors>
                            <bodyColors>
                                <bodyColor id="7">
                                      <name>Grey</name>
                                      <description>Vapour Grey</description>
                                      <paint id="1">solid</paint>
                                </bodyColor>
                            </bodyColors>
                        </colors>
                        <prices>
                            <price type="2" currency="GBP">
                                <grossPrice>29850</grossPrice>
                            </price>
                            <price type="4" currency="GBP">
                                <grossPrice>29850</grossPrice>
                            </price>
                        </prices>
                        <dates>
                            <date id="1">2025-02-20</date>
                            <date id="2">2025-06-16</date>
                        </dates>
                        <media>
                            <images url="https://picserver.eu-central-1.eu.mdxprod.io/userdata/1/46973/3FAdVwEMPE/">
                                <meta>
                                    <image name="xxl_kfz_112075572_1761134746_1.jpg" position="1" updatedDateTime="2025-10-22 14:05:47">
                                        <tags>
                                            <tag id="3"/>
                                        </tags>
                                    </image>
                                </meta>
                                <image name="xxl_kfz_112075572_1761134746_1.jpg" position="1" updatedDateTime="2025-10-22 14:05:47"/>
                            </images>
                        </media>
                        <options>
                            <option id="8">
                                <value>2</value>
                                <name>Air conditioning</name>
                                <description>Air conditioning</description>
                            </option>
                            <option id="928">
                                <value>82</value>
                                <name>82.0 kWh</name>
                                <description>Battery capacity</description>
                            </option>
                            <option id="927">
                                <value>30</value>
                                <name>30 mi</name>
                                <description>NEDC electric range</description>
                            </option>
                        </options>
                    </vehicle>
                </vehicles>';

        $xml_element = new SimpleXMLElement($xml);
        $vehicles = $xml_element->xpath('//vehicles/vehicle');
        $vehicle = reset($vehicles);

        $modix = new Modix();
        $methodMapVehicleData = new \ReflectionMethod(Modix::class, 'mapVehicleData');
        $methodGetStockLocations = new \ReflectionMethod(Modix::class, 'getAllStockLocations');
        $methodGetStockLocations->invoke($modix, 'foo');
        $vehicle_data = $methodMapVehicleData->invoke($modix, $vehicle);

        $this->assertEquals(82, $vehicle_data['battery_capacity_kwh']);
        $this->assertEquals(82, $vehicle_data['battery_usable_capacity_kwh']);
        $this->assertEquals(30, $vehicle_data['battery_range']);
    }

    public function testFeatures()
    {
        $vehicle = Vehicle::factory()->create();
        VehicleFeature::factory()->create([
            'vehicle_type' => 'vehicle',
            'vehicle_id' => $vehicle->id,
            'name' => 'foo',
        ]);
        VehicleFeature::factory()->create([
            'vehicle_type' => 'vehicle',
            'vehicle_id' => $vehicle->id,
            'name' => 'baz',
        ]);
        VehicleFeature::factory()->create([
            'vehicle_type' => 'vehicle',
            'vehicle_id' => $vehicle->id,
            'name' => 'bar',
        ]);
        VehicleFeature::factory()->create([
            'vehicle_type' => 'vehicle',
            'vehicle_id' => $vehicle->id + 1,
            'name' => 'other',
        ]);

        $updated_features = [
            'foo',
            'buzz',
        ];

        $modix = new Modix();
        $methodSyncFeatures = new \ReflectionMethod(Modix::class, 'syncFeatures');
        $methodSyncFeatures->invoke($modix, $vehicle, $updated_features);

        // assert that we didn't remove features for other vehicles
        $this->assertCount(1, VehicleFeature::query()->where('vehicle_id', '=', $vehicle->id + 1)->get());

        // assert that we removed features that don't exist in the incoming data
        $this->assertCount(2, VehicleFeature::query()->where('vehicle_id', '=', $vehicle->id)->get());
        $this->assertCount(1, VehicleFeature::query()->where('vehicle_id', '=', $vehicle->id)
            ->where('name', 'foo')->get());
        $this->assertCount(1, VehicleFeature::query()->where('vehicle_id', '=', $vehicle->id)
            ->where('name', 'buzz')->get());
    }

    public function testParseFormattedSpecialEquipmentWithHtmlList()
    {
        $modix = new Modix();
        $method = new \ReflectionMethod(Modix::class, 'parseFormattedSpecialEquipment');
        $method->setAccessible(true);

        $html = htmlentities('<ul><li>Heated seats</li><li>Parking sensors</li><li>Navigation</li></ul>');
        $result = $method->invoke($modix, $html);

        $this->assertCount(3, $result);
        $this->assertContains('Heated seats', $result);
        $this->assertContains('Parking sensors', $result);
        $this->assertContains('Navigation', $result);
    }

    public function testParseFormattedSpecialEquipmentDeduplicates()
    {
        $modix = new Modix();
        $method = new \ReflectionMethod(Modix::class, 'parseFormattedSpecialEquipment');
        $method->setAccessible(true);

        $html = htmlentities('<ul><li>Heated seats</li><li>Heated seats</li><li>Navigation</li></ul>');
        $result = $method->invoke($modix, $html);

        $this->assertCount(2, $result);
    }

    public function testParseFormattedSpecialEquipmentPlainText()
    {
        $modix = new Modix();
        $method = new \ReflectionMethod(Modix::class, 'parseFormattedSpecialEquipment');
        $method->setAccessible(true);

        $html = htmlentities('Just a plain text description');
        $result = $method->invoke($modix, $html);

        $this->assertCount(1, $result);
        $this->assertContains('Just a plain text description', $result);
    }

    public function testMapVehicleDataVehicleStatusAvailable()
    {
        $xml = $this->buildVehicleXml(['vehicleStatus' => 100]);
        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertFalse($vehicle_data['is_reserved']);
        $this->assertFalse($vehicle_data['is_sold']);
    }

    public function testMapVehicleDataVehicleStatusReserved()
    {
        $xml = $this->buildVehicleXml(['vehicleStatus' => 120]);
        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertTrue($vehicle_data['is_reserved']);
    }

    public function testMapVehicleDataVehicleStatusSold()
    {
        $xml = $this->buildVehicleXml(['vehicleStatus' => 130]);
        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertTrue($vehicle_data['is_sold']);
    }

    public function testMapVehicleDataPluginHybridFuelType()
    {
        $xml = $this->buildVehicleXml([], [
            ['id' => '999', 'value' => 'yes', 'name' => 'yes', 'description' => 'Plugin hybrid'],
        ]);

        $vehicle_data = $this->invokeMapVehicleData($xml);

        // fuel_type_id is null (mocked TaxonomyMapper) and filtered by array_filter.
        // Verify the method completes without error and returns valid data.
        $this->assertArrayHasKey('uuid', $vehicle_data);
        $this->assertArrayHasKey('is_published', $vehicle_data);
    }

    public function testMapVehicleDataExtractsDoors()
    {
        $xml = $this->buildVehicleXml([], [
            ['id' => '100', 'value' => '5', 'name' => '5', 'description' => 'Doors'],
        ]);

        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertEquals(5, $vehicle_data['door_count']);
    }

    public function testMapVehicleDataExtractsSeats()
    {
        $xml = $this->buildVehicleXml([], [
            ['id' => '101', 'value' => '7', 'name' => '7', 'description' => 'Seats'],
        ]);

        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertEquals(7, $vehicle_data['seats']);
    }

    public function testMapVehicleDataExtractsCapId()
    {
        $xml = $this->buildVehicleXml([], [
            ['id' => '200', 'value' => 'CAP12345', 'name' => 'CAP12345', 'description' => 'Cap ID'],
        ]);

        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertEquals('CAP12345', $vehicle_data['cap_id']);
    }

    public function testMapVehicleDataExtractsImages()
    {
        $xml = '<vehicles>
            <vehicle cipher="UUID-001" internalDescription="AB12CDE" modkey="LOC-1">
                <mainData>
                    <manufacturer>BMW</manufacturer>
                    <modelText>3 Series</modelText>
                    <submodel>320i</submodel>
                    <fuel>Petrol</fuel>
                    <transmission>Automatic</transmission>
                    <bodyStyle>Saloon</bodyStyle>
                    <category>Passenger car</category>
                    <usageCategory>Used vehicle</usageCategory>
                    <enginePower>184</enginePower>
                    <mileage>15000</mileage>
                    <cylinderCapacity>1998</cylinderCapacity>
                    <vehicleStatus>100</vehicleStatus>
                </mainData>
                <identification><vin>WBA12345678901234</vin></identification>
                <colors><bodyColors><bodyColor><name>Black</name><description>Sapphire Black</description></bodyColor></bodyColors></colors>
                <description>Test description</description>
                <prices><price><grossPrice>30000</grossPrice></price></prices>
                <dates></dates>
                <options></options>
                <media>
                    <images url="https://images.example.com/">
                        <image name="img1.jpg" position="1" />
                        <image name="img2.jpg" position="2" />
                        <image name="img3.jpg" position="3" />
                    </images>
                </media>
            </vehicle>
        </vehicles>';

        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertCount(3, $vehicle_data['images']);
        $this->assertContains('img1.jpg', $vehicle_data['images']);
        $this->assertContains('img2.jpg', $vehicle_data['images']);
        $this->assertContains('img3.jpg', $vehicle_data['images']);
    }

    public function testSyncEquipment()
    {
        $vehicle = Vehicle::factory()->create();

        $modix = new Modix();
        $method = new \ReflectionMethod(Modix::class, 'syncEquipment');
        $method->setAccessible(true);

        $equipment = ['Air conditioning', 'Power steering', 'Alloy wheels'];
        $method->invoke($modix, $vehicle, $equipment);

        $this->assertEquals(3, $vehicle->equipment()->count());
        $this->assertTrue($vehicle->equipment()->where('description', 'Air conditioning')->exists());
        $this->assertTrue($vehicle->equipment()->where('description', 'Power steering')->exists());
        $this->assertTrue($vehicle->equipment()->where('description', 'Alloy wheels')->exists());
    }

    public function testSyncEquipmentDoesNotDuplicate()
    {
        $vehicle = Vehicle::factory()->create();

        $modix = new Modix();
        $method = new \ReflectionMethod(Modix::class, 'syncEquipment');
        $method->setAccessible(true);

        $equipment = ['Air conditioning', 'Power steering'];
        $method->invoke($modix, $vehicle, $equipment);
        $method->invoke($modix, $vehicle, $equipment);

        $this->assertEquals(2, $vehicle->equipment()->count());
    }

    public function testEnabled()
    {
        Settings::make([
            'tab' => 'foo', 'section' => 'foo', 'group' => 'foo', 'name' => 'foo',
            'config_key' => 'stock-modix-enabled', 'value' => false, 'type' => 'boolean',
        ]);
        $this->assertFalse((new Modix())->enabled());

        Settings::update('stock-modix-enabled', true);
        $this->assertTrue((new Modix())->enabled());
    }

    public function testDealershipAdditionalDataFields()
    {
        $fields = (new Modix())->dealershipAdditionalDataFields();
        $this->assertArrayHasKey('modix-location-id', $fields);
    }

    public function testMapVehicleDataUsedVehicle()
    {
        $xml = $this->buildVehicleXml(['usageCategory' => 'Used vehicle']);
        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertEquals(0, $vehicle_data['is_new']);
    }

    public function testMapVehicleDataNewVehicle()
    {
        $xml = $this->buildVehicleXml(['usageCategory' => 'New vehicle']);
        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertEquals(1, $vehicle_data['is_new']);
    }

    public function testMapVehicleDataExtractsSpecialEquipment()
    {
        $xml = $this->buildVehicleXml([], [
            ['id' => '300', 'value' => 'Heated seats, Parking sensors', 'name' => 'Heated seats, Parking sensors', 'description' => 'Special equipment'],
        ]);

        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertArrayHasKey('features', $vehicle_data);
        $this->assertContains('Heated seats', $vehicle_data['features']);
        $this->assertContains('Parking sensors', $vehicle_data['features']);
    }

    public function testMapVehicleDataExtractsStandardEquipment()
    {
        $xml = $this->buildVehicleXml([], [
            ['id' => '400', 'value' => 'ABS, ESP, Airbags', 'name' => 'ABS, ESP, Airbags', 'description' => 'Standard equipment'],
        ]);

        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertArrayHasKey('equipment', $vehicle_data);
        $this->assertContains('ABS', $vehicle_data['equipment']);
        $this->assertContains('ESP', $vehicle_data['equipment']);
        $this->assertContains('Airbags', $vehicle_data['equipment']);
    }

    public function testMapVehicleDataExtractsRegistrationFromTechData()
    {
        $xml = $this->buildVehicleXml(['internalDescription' => ''], [
            ['id' => '500', 'value' => 'AB23CDE', 'name' => 'AB23CDE', 'description' => 'Registration plate number'],
        ]);

        $vehicle_data = $this->invokeMapVehicleData($xml);

        $this->assertEquals('AB23CDE', $vehicle_data['registration_number']);
    }

    public function testMapVehicleDataPriceExceedingMaxIsNull()
    {
        $xml = $this->buildVehicleXml(['grossPrice' => '2147483647']);
        $vehicleData = $this->invokeMapVehicleData($xml);

        $this->assertArrayNotHasKey('price', $vehicleData);
    }

    public function testMapVehicleDataValidPriceIsKept()
    {
        $xml = $this->buildVehicleXml();
        $vehicleData = $this->invokeMapVehicleData($xml);

        $this->assertEquals(30000.0, $vehicleData['price']);
    }

    public function testSyncFeaturesTruncatesLongNames()
    {
        $vehicle = Vehicle::factory()->create();
        $longFeatureName = str_repeat('A', 300);

        $modix = new Modix();
        $method = new \ReflectionMethod(Modix::class, 'syncFeatures');
        $method->setAccessible(true);
        $method->invoke($modix, $vehicle, [$longFeatureName]);

        $feature = VehicleFeature::query()->where('vehicle_id', $vehicle->id)->first();
        $this->assertNotNull($feature);
        $this->assertLessThanOrEqual(193, strlen($feature->name)); // 190 + '...'
    }

    public function testSyncFeaturesTruncatedNamesAreConsistentAcrossSyncs()
    {
        $vehicle = Vehicle::factory()->create();
        $longFeatureName = str_repeat('B', 300);

        $modix = new Modix();
        $method = new \ReflectionMethod(Modix::class, 'syncFeatures');
        $method->setAccessible(true);

        $method->invoke($modix, $vehicle, [$longFeatureName]);
        $countAfterFirstSync = VehicleFeature::query()->where('vehicle_id', $vehicle->id)->count();

        $method->invoke($modix, $vehicle, [$longFeatureName]);
        $countAfterSecondSync = VehicleFeature::query()->where('vehicle_id', $vehicle->id)->count();

        $this->assertEquals(1, $countAfterFirstSync);
        $this->assertEquals(1, $countAfterSecondSync);
    }

    private function buildVehicleXml(array $mainDataOverrides = [], array $extraOptions = []): string
    {
        $defaults = [
            'manufacturer' => 'BMW',
            'modelText' => '3 Series',
            'submodel' => '320i',
            'fuel' => 'Petrol',
            'transmission' => 'Automatic',
            'bodyStyle' => 'Saloon',
            'category' => 'Passenger car',
            'usageCategory' => 'Used vehicle',
            'enginePower' => '184',
            'mileage' => '15000',
            'cylinderCapacity' => '1998',
            'vehicleStatus' => '100',
        ];

        $mainData = array_merge($defaults, $mainDataOverrides);
        $internalDesc = $mainDataOverrides['internalDescription'] ?? 'AB12CDE';
        $grossPrice = $mainDataOverrides['grossPrice'] ?? '30000';

        $optionsXml = '';
        foreach ($extraOptions as $opt) {
            $optionsXml .= sprintf(
                '<option id="%s"><value>%s</value><name>%s</name><description>%s</description></option>',
                $opt['id'],
                htmlspecialchars($opt['value']),
                htmlspecialchars($opt['name']),
                htmlspecialchars($opt['description'])
            );
        }

        return '<vehicles>
            <vehicle cipher="UUID-001" internalDescription="' . $internalDesc . '" modkey="LOC-1">
                <mainData>
                    <manufacturer>' . $mainData['manufacturer'] . '</manufacturer>
                    <modelText>' . $mainData['modelText'] . '</modelText>
                    <submodel>' . $mainData['submodel'] . '</submodel>
                    <fuel>' . $mainData['fuel'] . '</fuel>
                    <transmission>' . $mainData['transmission'] . '</transmission>
                    <bodyStyle>' . $mainData['bodyStyle'] . '</bodyStyle>
                    <category>' . $mainData['category'] . '</category>
                    <usageCategory>' . $mainData['usageCategory'] . '</usageCategory>
                    <enginePower>' . $mainData['enginePower'] . '</enginePower>
                    <mileage>' . $mainData['mileage'] . '</mileage>
                    <cylinderCapacity>' . $mainData['cylinderCapacity'] . '</cylinderCapacity>
                    <vehicleStatus>' . $mainData['vehicleStatus'] . '</vehicleStatus>
                </mainData>
                <identification><vin>WBA12345678901234</vin></identification>
                <colors><bodyColors><bodyColor><name>Black</name><description>Sapphire Black</description></bodyColor></bodyColors></colors>
                <description>Test description</description>
                <prices><price><grossPrice>' . $grossPrice . '</grossPrice></price></prices>
                <dates><date id="1">2023-01-15</date></dates>
                <options>' . $optionsXml . '</options>
                <media><images url="https://images.example.com/"></images></media>
            </vehicle>
        </vehicles>';
    }

    private function invokeMapVehicleData(string $xml): array
    {
        $xml_element = new SimpleXMLElement($xml);
        $vehicles = $xml_element->xpath('//vehicles/vehicle');
        $vehicle = reset($vehicles);

        $modix = new Modix();
        $methodMapVehicleData = new \ReflectionMethod(Modix::class, 'mapVehicleData');
        $methodGetStockLocations = new \ReflectionMethod(Modix::class, 'getAllStockLocations');
        $methodMapVehicleData->setAccessible(true);
        $methodGetStockLocations->setAccessible(true);
        $methodGetStockLocations->invoke($modix, 'foo');

        return $methodMapVehicleData->invoke($modix, $vehicle);
    }
}
