<?php

namespace Tests\Tenant;

use App\Imports\VehicleImport;
use App\Models\ImportMap;
use App\Models\ImportMapField;
use App\TaxonomyMap;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Tests\DatabaseTestCase;

class CastToModelFromTitleTest extends DatabaseTestCase
{
    private VehicleImport $import;
    private \ReflectionMethod $method;

    private VehicleModel $trend;
    private VehicleModel $xlAFamily;
    private VehicleModel $xl;
    private VehicleModel $sprinter;
    private VehicleModel $bClass;
    private VehicleModel $focus;

    protected function setUp(): void
    {
        parent::setUp();

        // Create makes
        $dethleffs = VehicleMake::factory()->create(['name' => 'Dethleffs']);
        $mercedes = VehicleMake::factory()->create(['name' => 'Mercedes-Benz']);
        $ford = VehicleMake::factory()->create(['name' => 'Ford']);

        // Create models for Dethleffs
        $this->trend = VehicleModel::factory()->create(['name' => 'Trend', 'make_id' => $dethleffs->id]);
        $this->xlAFamily = VehicleModel::factory()->create(['name' => 'XL A Family', 'make_id' => $dethleffs->id]);
        $this->xl = VehicleModel::factory()->create(['name' => 'XL', 'make_id' => $dethleffs->id]);

        // Create models for Mercedes-Benz
        $this->sprinter = VehicleModel::factory()->create(['name' => 'Sprinter', 'make_id' => $mercedes->id]);
        $this->bClass = VehicleModel::factory()->create(['name' => 'B-Class ModernComfort', 'make_id' => $mercedes->id]);

        // Create models for Ford
        $this->focus = VehicleModel::factory()->create(['name' => 'Focus', 'make_id' => $ford->id]);

        // Create import map with make field
        $importMap = ImportMap::factory()->create();
        ImportMapField::create([
            'map_id' => $importMap->id,
            'on_file' => 'make',
            'on_model' => 'make_id',
        ]);
        $importMap->load('fields');

        $providerName = tenant('id') . '-import-map-' . $importMap->id;

        // Make taxonomy maps — alternative names that resolve to the same make
        TaxonomyMap::create([
            'provider' => $providerName,
            'taxonomy_type' => 'master-make',
            'term' => 'DETHLEFF',
            'taxonomy_id' => $dethleffs->id,
        ]);
        TaxonomyMap::create([
            'provider' => $providerName,
            'taxonomy_type' => 'master-make',
            'term' => 'Dethleff Motors',
            'taxonomy_id' => $dethleffs->id,
        ]);

        // Model taxonomy maps — alternative model terms
        TaxonomyMap::create([
            'provider' => $providerName,
            'taxonomy_type' => 'master-model',
            'term' => 'BMC-I',
            'taxonomy_id' => $this->bClass->id,
            'parent_id' => $mercedes->id,
        ]);

        // Build import instance via reflection to avoid constructor side effects
        $ref = new \ReflectionClass(VehicleImport::class);
        $this->import = $ref->newInstanceWithoutConstructor();

        // Set importType
        $ref->getProperty('importType')->setValue($this->import, 'default');

        // Set import_map
        $ref->getProperty('import_map')->setValue($this->import, $importMap);

        // Set makes collection (pluck('id', 'name') format)
        $ref->getProperty('makes')->setValue($this->import, collect([
            'Dethleffs' => $dethleffs->id,
            'Mercedes-Benz' => $mercedes->id,
            'Ford' => $ford->id,
        ]));

        // Set remaining relationship collections to empty
        foreach (['dealerships', 'models', 'fuel_types', 'body_styles', 'drivetrains', 'transmissions'] as $prop) {
            $ref->getProperty($prop)->setValue($this->import, collect());
        }

        // Set vehicleAttributes and subFieldDelimiter
        (new \ReflectionProperty($this->import, 'vehicleAttributes'))->setValue($this->import, []);
        (new \ReflectionProperty($this->import, 'subFieldDelimiter'))->setValue($this->import, '|');

        // Set static vehicleFillable if empty
        $fillableProp = $ref->getProperty('vehicleFillable');
        if (empty($fillableProp->getValue())) {
            $fillableProp->setValue(null, (new Vehicle())->getFillable());
        }

        $this->method = new \ReflectionMethod($this->import, 'castToModelFromTitle');
    }

    private function castToModelFromTitle(string $value, array $row): ?int
    {
        return $this->method->invoke($this->import, $value, $row);
    }

    public function testCastToModelFromTitle()
    {
        // 1. Make field missing from row → null
        $this->assertNull(
            $this->castToModelFromTitle('Anything', []),
            'Should return null when make column is missing from the row'
        );

        // 2. Make not resolvable → null
        $this->assertNull(
            $this->castToModelFromTitle('Unknown Model', ['make' => 'NonExistent']),
            'Should return null when the make name cannot be resolved'
        );

        // 3. Direct match — file make in title, model matches
        $this->assertEquals(
            $this->trend->id,
            $this->castToModelFromTitle('Dethleffs Trend A7877-2 DBM', ['make' => 'Dethleffs']),
            'Should match model directly when file make name is in the title'
        );

        // 4. Case-insensitive title matching
        $this->assertEquals(
            $this->trend->id,
            $this->castToModelFromTitle('dethleffs trend a7877-2', ['make' => 'Dethleffs']),
            'Should match model case-insensitively'
        );

        // 5. DB make name in title, file make column differs
        //    File: "DETHLEFF", DB: "Dethleffs", Title: "Dethleffs XL A Family A 7872-2"
        //    "DETHLEFF" is rejected (word boundary: followed by 's' not space)
        //    "Dethleffs" strips cleanly → "XL A Family A 7872-2" → matches "XL A Family"
        $this->assertEquals(
            $this->xlAFamily->id,
            $this->castToModelFromTitle('Dethleffs XL A Family A 7872-2', ['make' => 'DETHLEFF']),
            'Should strip using DB make name when file make name does not word-boundary match'
        );

        // 6. Taxonomy make term used in title
        //    File: "Dethleffs", Title: "Dethleff Motors Trend A7877"
        //    "Dethleffs" doesn't start "Dethleff Motors..." (word boundary fails)
        //    Taxonomy term "Dethleff Motors" strips cleanly → "Trend A7877" → matches "Trend"
        $this->assertEquals(
            $this->trend->id,
            $this->castToModelFromTitle('Dethleff Motors Trend A7877', ['make' => 'Dethleffs']),
            'Should strip using taxonomy make term when it appears in the title'
        );

        // 7. Word boundary protection — partial make name rejected
        //    File: "Ford", Title: "Fordson Major"
        //    "Ford" + "son" → not a word boundary → skip → no match
        $this->assertNull(
            $this->castToModelFromTitle('Fordson Major', ['make' => 'Ford']),
            'Should not strip make name when it is only a substring (no word boundary)'
        );

        // 8. Longest make name tried first
        //    "Mercedes-Benz" (13 chars) before any shorter variant
        $this->assertEquals(
            $this->sprinter->id,
            $this->castToModelFromTitle('Mercedes-Benz Sprinter 314', ['make' => 'Mercedes-Benz']),
            'Should match model after stripping the longest make name first'
        );

        // 9. Longer model name preferred over shorter
        //    Both "XL" and "XL A Family" start "xl a family..." but "XL A Family" is longer
        $this->assertEquals(
            $this->xlAFamily->id,
            $this->castToModelFromTitle('Dethleffs XL A Family A 7872-2', ['make' => 'Dethleffs']),
            'Should prefer longer model name match over shorter'
        );

        // 10. Model taxonomy term match (BMC-I → B-Class ModernComfort)
        $this->assertEquals(
            $this->bClass->id,
            $this->castToModelFromTitle('Mercedes-Benz BMC-I 2024', ['make' => 'Mercedes-Benz']),
            'Should match model via taxonomy map term when no DB model name matches'
        );

        // 11. DB model name preferred over taxonomy term
        //     "Sprinter" is a real DB model name — matched before taxonomy terms are checked
        $this->assertEquals(
            $this->sprinter->id,
            $this->castToModelFromTitle('Mercedes-Benz Sprinter 314', ['make' => 'Mercedes-Benz']),
            'Should prefer DB model name over taxonomy term'
        );

        // 12. Make resolved via taxonomy mapping (not in makes collection)
        //     "DETHLEFF" is not in $this->makes, but a taxonomy map maps it to Dethleffs' ID
        //     Then DB make name "Dethleffs" is found via reverse lookup for stripping
        $this->assertEquals(
            $this->trend->id,
            $this->castToModelFromTitle('Dethleffs Trend A7877-2 DBM', ['make' => 'DETHLEFF']),
            'Should resolve make via taxonomy mapping and use DB make name for stripping'
        );

        // 13. Title equals make name only — no model remaining
        $this->assertNull(
            $this->castToModelFromTitle('Dethleffs', ['make' => 'Dethleffs']),
            'Should return null when title is just the make name with no model'
        );
    }
}
