<?php

namespace Tests\Tenant;

use App\Models\ExportMap;
use App\ExportMapRepository;
use Mtc\ContentManager\Facades\Media;
use Mtc\ContentManager\Models\Media as MediaModel;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Mtc\MercuryDataModels\VehicleOffer;
use Tests\TenantTestCase;
use Carbon\Carbon;

class ExportMapRepositoryTest extends TenantTestCase
{
    public function test_column_count_and_header_mapping_is_correct()
    {
        $vehicle = Vehicle::factory()->create([
            'registration_number' => 'TEST01',
            'vin' => 'VIN12345',
        ]);

        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['registration_number', 'vin'],
            'headers' => ['Reg Number', 'VIN Number'],
        ]);

        // 1. Test column count
        $this->assertCount(2, $mappedData);

        // 2. Test that columns match headers in order
        $this->assertEquals($vehicle->registration_number, $mappedData[0]);
        $this->assertEquals($vehicle->vin, $mappedData[1]);
    }

    /**
     * Helper method to run the mapping process for a single entry.
     * It uses Reflection to test the protected 'mapEntries' method directly.
     *
     * @param Vehicle|VehicleOffer $entry The data entry to process.
     * @param array $mapConfig The configuration for the ExportMap.
     * @param string $type The export map type.
     * @return array The resulting mapped data row.
     */
    private function runMapProcessing(Vehicle|VehicleOffer $entry, array $mapConfig, string $type = 'vehicle'): array
    {
        $config = array_merge([
            'name' => 'Test Map',
            'type' => $type,
            'columns' => [],
            'mapping_rules' => [],
            'headers' => [],
        ], $mapConfig);

        $exportMap = new ExportMap($config);
        $repository = new ExportMapRepository($exportMap);

        $method = new \ReflectionMethod(ExportMapRepository::class, 'mapEntries');

        $resultCollection = $method->invoke($repository, collect([$entry]));

        return $resultCollection->first() ?? [];
    }

    // --- Operator Tests ---

    public function test_operator_equals()
    {
        $vehicle = Vehicle::factory()->create(['type' => 'Car']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'type', 'op' => 'eq', 'value' => 'Car'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Is a Car']],
            ]],
        ]);
        $this->assertEquals('Is a Car', $mappedData[0]);

        $vehicle->type = 'LCV';
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'type', 'op' => 'eq', 'value' => 'Car'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Is a Car']],
            ]],
        ]);
        $this->assertNull($mappedData[0]);
    }

    public function test_operator_not_equals()
    {
        $vehicle = Vehicle::factory()->create(['type' => 'LCV']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'type', 'op' => 'neq', 'value' => 'Car'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Not a Car']],
            ]],
        ]);
        $this->assertEquals('Not a Car', $mappedData[0]);
    }

    public function test_operator_is_any_of()
    {
        $vehicle = Vehicle::factory()->create(['colour' => 'Blue']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'colour', 'op' => 'in', 'value' => 'Red,Green,Blue'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Primary Color']],
            ]],
        ]);
        $this->assertEquals('Primary Color', $mappedData[0]);
    }

    public function test_operator_is_none_of()
    {
        $vehicle = Vehicle::factory()->create(['colour' => 'Black']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'colour', 'op' => 'not_in', 'value' => 'Red,Green,Blue'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Not a Primary Color']],
            ]],
        ]);
        $this->assertEquals('Not a Primary Color', $mappedData[0]);
    }

    public function test_operator_contains()
    {
        $vehicle = Vehicle::factory()->create(['description' => 'A beautiful family car']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'description', 'op' => 'contains', 'value' => 'family'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Contains Keyword']],
            ]],
        ]);
        $this->assertEquals('Contains Keyword', $mappedData[0]);
    }

    public function test_operator_starts_with()
    {
        $vehicle = Vehicle::factory()->create(['vin' => 'VIN12345']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'vin', 'op' => 'starts_with', 'value' => 'VIN'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Prefix Match']],
            ]],
        ]);
        $this->assertEquals('Prefix Match', $mappedData[0]);
    }

    public function test_operator_ends_with()
    {
        $vehicle = Vehicle::factory()->create(['vin' => 'VIN12345XYZ']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'vin', 'op' => 'ends_with', 'value' => 'XYZ'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Suffix Match']],
            ]],
        ]);
        $this->assertEquals('Suffix Match', $mappedData[0]);
    }

    public function test_operator_greater_than()
    {
        $vehicle = Vehicle::factory()->create(['price' => 20000]);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'price', 'op' => 'gt', 'value' => '15000'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Expensive']],
            ]],
        ]);
        $this->assertEquals('Expensive', $mappedData[0]);
    }

    public function test_operator_greater_or_equal()
    {
        $vehicle = Vehicle::factory()->create(['manufacture_year' => 2022]);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'manufacture_year', 'op' => 'gte', 'value' => '2022'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Modern Car']],
            ]],
        ]);
        $this->assertEquals('Modern Car', $mappedData[0]);
    }

    public function test_operator_less_than()
    {
        $vehicle = Vehicle::factory()->create(['odometer_mi' => 50000]);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'odometer_mi', 'op' => 'lt', 'value' => '60000'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Low Mileage']],
            ]],
        ]);
        $this->assertEquals('Low Mileage', $mappedData[0]);
    }

    public function test_operator_less_or_equal()
    {
        $vehicle = Vehicle::factory()->create(['previous_owner_count' => 1]);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'previous_owner_count', 'op' => 'lte', 'value' => '1'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Few Owners']],
            ]],
        ]);
        $this->assertEquals('Few Owners', $mappedData[0]);
    }

    public function test_operator_is_empty()
    {
        $vehicle = Vehicle::factory()->create(['trim' => null]);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'trim', 'op' => 'is_empty', 'value' => null],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Trim Not Specified']],
            ]],
        ]);
        $this->assertEquals('Trim Not Specified', $mappedData[0]);
    }

    public function test_operator_is_not_empty()
    {
        $vehicle = Vehicle::factory()->create(['trim' => 'Sportline']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'trim', 'op' => 'is_not_empty', 'value' => null],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Trim Is Specified']],
            ]],
        ]);
        $this->assertEquals('Trim Is Specified', $mappedData[0]);
    }

    // --- Other Tests ---

    public function test_value_casting_for_export()
    {
        $date = Carbon::create(2025, 5, 20);
        $vehicle = Vehicle::factory()->create([
            'is_published' => true,
            'is_sold' => false,
            'first_registration_date' => $date,
        ]);

        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['is_published', 'is_sold', 'first_registration_date'],
        ]);

        $this->assertEquals('Y', $mappedData[0]);
        $this->assertEquals('N', $mappedData[1]);
        $this->assertEquals('2025-05-20', $mappedData[2]);
    }

    public function test_relationship_and_custom_attribute_mapping()
    {
        $make = VehicleMake::factory()->create(['name' => 'Volkswagen']);
        $model = VehicleModel::factory()->create(['name' => 'Golf']);
        $vehicle = Vehicle::factory()->create(['make_id' => $make->id, 'model_id' => $model->id]);

        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['make.name', 'model.name', 'empty'],
        ]);

        $this->assertEquals('Volkswagen', $mappedData[0]);
        $this->assertEquals('Golf', $mappedData[1]);
        $this->assertEquals('', $mappedData[2]);
    }

    // --- Offer Tests ---

    public function test_offer_column_mapping()
    {
        $make = VehicleMake::factory()->create(['name' => 'BMW']);
        $model = VehicleModel::factory()->create(['name' => 'X5']);
        $offer = VehicleOffer::factory()->create([
            'name' => 'Test Offer',
            'make_id' => $make->id,
            'model_id' => $model->id,
            'price' => 29999,
        ]);

        $mappedData = $this->runMapProcessing($offer, [
            'columns' => ['name', 'make.name', 'model.name', 'price'],
        ], 'offer');

        $this->assertCount(4, $mappedData);
        $this->assertEquals('Test Offer', $mappedData[0]);
        $this->assertEquals('BMW', $mappedData[1]);
        $this->assertEquals('X5', $mappedData[2]);
        $this->assertEquals(29999, $mappedData[3]);
    }

    public function test_offer_mapping_rules_apply()
    {
        $offer = VehicleOffer::factory()->create([
            'price' => 35000,
        ]);

        $mappedData = $this->runMapProcessing($offer, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'price', 'op' => 'gt', 'value' => '30000'],
                'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Premium']],
            ]],
        ], 'offer');

        $this->assertEquals('Premium', $mappedData[0]);
    }

    // --- Append/Prepend Action Tests ---

    public function test_action_append_adds_suffix_to_field_value()
    {
        $vehicle = Vehicle::factory()->create(['price' => 25000]);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['price'],
            'mapping_rules' => [[
                'when' => ['field' => 'price', 'op' => 'is_not_empty', 'value' => null],
                'then' => [['action' => 'append', 'target' => 'price', 'value' => ' GBP']],
            ]],
        ]);
        $this->assertEquals('25000 GBP', $mappedData[0]);
    }

    public function test_action_prepend_adds_prefix_to_field_value()
    {
        $vehicle = Vehicle::factory()->create(['price' => 25000]);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['price'],
            'mapping_rules' => [[
                'when' => ['field' => 'price', 'op' => 'is_not_empty', 'value' => null],
                'then' => [['action' => 'prepend', 'target' => 'price', 'value' => '£']],
            ]],
        ]);
        $this->assertEquals('£25000', $mappedData[0]);
    }

    public function test_action_append_does_not_apply_when_condition_not_met()
    {
        $vehicle = Vehicle::factory()->create(['trim' => null]);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['trim'],
            'mapping_rules' => [[
                'when' => ['field' => 'trim', 'op' => 'is_not_empty', 'value' => null],
                'then' => [['action' => 'append', 'target' => 'trim', 'value' => ' Sport']],
            ]],
        ]);
        $this->assertNull($mappedData[0]);
    }

    public function test_action_append_skips_when_wrong_type()
    {
        $vehicle = Vehicle::factory()->create(['type' => 'LCV']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [[
                'when' => ['field' => 'type', 'op' => 'eq', 'value' => 'Car'],
                'then' => [['action' => 'append', 'target' => 'custom1', 'value' => ' suffix']],
            ]],
        ]);
        $this->assertNull($mappedData[0]);
    }

    public function test_action_append_works_with_set_in_sequence()
    {
        $vehicle = Vehicle::factory()->create(['type' => 'Car']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [
                [
                    'when' => ['field' => 'type', 'op' => 'eq', 'value' => 'Car'],
                    'then' => [['action' => 'set', 'target' => 'custom1', 'value' => '25000']],
                ],
                [
                    'when' => ['field' => 'type', 'op' => 'eq', 'value' => 'Car'],
                    'then' => [['action' => 'append', 'target' => 'custom1', 'value' => ' GBP']],
                ],
            ],
        ]);
        $this->assertEquals('25000 GBP', $mappedData[0]);
    }

    public function test_action_prepend_works_after_set()
    {
        $vehicle = Vehicle::factory()->create(['type' => 'Car']);
        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['custom1'],
            'mapping_rules' => [
                [
                    'when' => ['field' => 'type', 'op' => 'eq', 'value' => 'Car'],
                    'then' => [['action' => 'set', 'target' => 'custom1', 'value' => 'Hello']],
                ],
                [
                    'when' => ['field' => 'type', 'op' => 'eq', 'value' => 'Car'],
                    'then' => [['action' => 'prepend', 'target' => 'custom1', 'value' => 'Say: ']],
                ],
            ],
        ]);
        $this->assertEquals('Say: Hello', $mappedData[0]);
    }

    // --- Individual Image Column Tests ---

    public function test_image_1_returns_primary_image()
    {
        $vehicle = Vehicle::factory()->create();
        $media1 = MediaModel::factory()->create(['src' => 'img1.jpg']);
        $media2 = MediaModel::factory()->create(['src' => 'img2.jpg']);
        $media3 = MediaModel::factory()->create(['src' => 'img3.jpg']);

        $vehicle->mediaUses()->create(['media_id' => $media1->id, 'order' => 1]);
        $vehicle->mediaUses()->create(['media_id' => $media2->id, 'order' => 2]);
        $vehicle->mediaUses()->create(['media_id' => $media3->id, 'order' => 3]);
        $vehicle->load('mediaUses.media');

        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['image_1'],
        ]);

        $this->assertEquals($media1->getOriginalUrlAttribute(true), $mappedData[0]);
    }

    public function test_image_2_returns_second_image()
    {
        $vehicle = Vehicle::factory()->create();
        $media1 = MediaModel::factory()->create(['src' => 'img1.jpg']);
        $media2 = MediaModel::factory()->create(['src' => 'img2.jpg']);
        $media3 = MediaModel::factory()->create(['src' => 'img3.jpg']);

        $vehicle->mediaUses()->create(['media_id' => $media1->id, 'order' => 1]);
        $vehicle->mediaUses()->create(['media_id' => $media2->id, 'order' => 2]);
        $vehicle->mediaUses()->create(['media_id' => $media3->id, 'order' => 3]);
        $vehicle->load('mediaUses.media');

        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['image_2'],
        ]);

        $this->assertEquals($media2->getOriginalUrlAttribute(true), $mappedData[0]);
    }

    public function test_image_column_returns_empty_when_no_image_at_index()
    {
        $vehicle = Vehicle::factory()->create();
        $media1 = MediaModel::factory()->create(['src' => 'img1.jpg']);
        $media2 = MediaModel::factory()->create(['src' => 'img2.jpg']);

        $vehicle->mediaUses()->create(['media_id' => $media1->id, 'order' => 1]);
        $vehicle->mediaUses()->create(['media_id' => $media2->id, 'order' => 2]);
        $vehicle->load('mediaUses.media');

        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['image_5'],
        ]);

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

    public function test_multiple_individual_image_columns()
    {
        $vehicle = Vehicle::factory()->create();
        $media1 = MediaModel::factory()->create(['src' => 'img1.jpg']);
        $media2 = MediaModel::factory()->create(['src' => 'img2.jpg']);
        $media3 = MediaModel::factory()->create(['src' => 'img3.jpg']);

        $vehicle->mediaUses()->create(['media_id' => $media1->id, 'order' => 1]);
        $vehicle->mediaUses()->create(['media_id' => $media2->id, 'order' => 2]);
        $vehicle->mediaUses()->create(['media_id' => $media3->id, 'order' => 3]);
        $vehicle->load('mediaUses.media');

        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['image_1', 'image_2', 'image_3'],
        ]);

        $this->assertCount(3, $mappedData);
        $this->assertEquals($media1->getOriginalUrlAttribute(true), $mappedData[0]);
        $this->assertEquals($media2->getOriginalUrlAttribute(true), $mappedData[1]);
        $this->assertEquals($media3->getOriginalUrlAttribute(true), $mappedData[2]);
    }

    public function test_image_columns_respect_media_use_order()
    {
        $vehicle = Vehicle::factory()->create();
        $media1 = MediaModel::factory()->create(['src' => 'first.jpg']);
        $media2 = MediaModel::factory()->create(['src' => 'second.jpg']);
        $media3 = MediaModel::factory()->create(['src' => 'third.jpg']);

        $vehicle->mediaUses()->create(['media_id' => $media1->id, 'order' => 3]);
        $vehicle->mediaUses()->create(['media_id' => $media2->id, 'order' => 1]);
        $vehicle->mediaUses()->create(['media_id' => $media3->id, 'order' => 2]);
        $vehicle->load('mediaUses.media');

        $mappedData = $this->runMapProcessing($vehicle, [
            'columns' => ['image_1', 'image_2', 'image_3'],
        ]);

        // Sorted by order: media2 (order 1), media3 (order 2), media1 (order 3)
        $this->assertEquals($media2->getOriginalUrlAttribute(true), $mappedData[0]);
        $this->assertEquals($media3->getOriginalUrlAttribute(true), $mappedData[1]);
        $this->assertEquals($media1->getOriginalUrlAttribute(true), $mappedData[2]);
    }
}
