<?php

namespace Tests\Feature\Admin;

use App\Http\Middleware\UUIDCheckMiddleware;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Laravel\Sanctum\Sanctum;
use Tests\Support\CreatesAdminApiUser;
use Tests\Support\CreatesStationImportExcel;
use Tests\TestCase;

class ImportEstacionesTest extends TestCase
{
    use CreatesAdminApiUser;
    use CreatesStationImportExcel;
    use RefreshDatabase;
    
    private const ENDPOINT = '/api/admin/estaciones/import';
    private const SKIPPED_ENDPOINT = '/api/admin/estaciones/import/skipped';

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

        $this->withoutMiddleware(UUIDCheckMiddleware::class);
        $this->withHeader('X-Requested-With', 'XMLHttpRequest');
    }

    /**
     * Evita accesos anónimos al endpoint administrativo.
     */
    public function test_requires_authentication(): void
    {
        $response = $this->postJson(self::ENDPOINT);

        $response->assertUnauthorized();
    }

    /**
     * Verifica que sólo un root user pueda ejecutar un reemplazo masivo.
     */
    public function test_requires_root_permissions(): void
    {
        [$admin] = $this->createAdminApiUser(false);
        Sanctum::actingAs($admin, ['admin']);

        $response = $this->post(self::ENDPOINT, [
                'file' => $this->createStationsExcel([]),
                'mapping' => $this->mapping(),
            ]);

        $response->assertStatus(406);
    }

    /**
     * Valida la presencia del archivo antes de entrar al servicio.
     */
    public function test_validates_the_uploaded_file(): void
    {
        [$admin] = $this->createAdminApiUser();
        Sanctum::actingAs($admin, ['admin']);

        $response = $this->postJson(self::ENDPOINT, [
            'mapping' => $this->mapping(),
        ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['file']);
    }

    /**
     * Cubre el flujo completo con middlewares reales y reemplazo de datos.
     */
    public function test_import_endpoint_replaces_existing_stations(): void
    {
        [$admin] = $this->createAdminApiUser();
        Sanctum::actingAs($admin, ['admin']);
        $cityId = $this->createCatalogCity('Asunción', 'Capital');

        $oldStationId = DB::query()->from('estaciones')->insertGetId([
            'titulo' => 'Anterior',
            'slug' => 'anterior',
            'estado' => true,
            'ciudad_id' => $cityId,
            'ubicacion' => json_encode(['lat' => -25.3, 'lng' => -57.6]),
            'direccion' => 'Registro anterior',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        DB::query()->from('estaciones_servicios')->insert([
            'estacion_id' => $oldStationId,
            'servicio_id' => $this->createService(),
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $response = $this->post(self::ENDPOINT, [
                'file' => $this->createStationsExcel([
                    [1, 'Avda. Primer Presidente', '-25.25595, -57.57174', 'Asunción', 'Capital', '', '', '', '', ''],
                ]),
                'mapping' => $this->mapping(),
            ]);

        $response->assertOk();
        $response->assertJson([
            'imported_rows' => 1,
            'skipped_rows' => 0,
        ]);

        $this->assertDatabaseCount('estaciones', 1);
        $this->assertDatabaseMissing('estaciones', ['direccion' => 'Registro anterior']);
        $this->assertDatabaseCount('estaciones_servicios', 0);
    }

    /**
     * Evita imports con mapeos manipulados desde cliente.
     */
    public function test_rejects_invalid_mapping_columns(): void
    {
        [$admin] = $this->createAdminApiUser();
        Sanctum::actingAs($admin, ['admin']);

        $response = $this->post(self::ENDPOINT, [
            'file' => $this->createStationsExcel([
                [1, 'Avda. Primer Presidente', '-25.25595, -57.57174', 'Asunción', 'Capital', '', '', '', '', ''],
            ]),
            'mapping' => [
                ...$this->mapping(),
                'ciudad' => 'columna_inexistente',
            ],
        ]);

        $response->assertStatus(422);
        $response->assertJson([
            'message' => 'El archivo no tiene el formato esperado para importar estaciones.',
        ]);
    }

    /**
     * Conserva la fila original omitida para que operación pueda corregirla y
     * regenerar una planilla con la misma estructura del Excel de entrada.
     */
    public function test_returns_original_row_payload_for_skipped_records(): void
    {
        [$admin] = $this->createAdminApiUser();
        Sanctum::actingAs($admin, ['admin']);
        $this->createCatalogCity('Asunción', 'Capital');

        $response = $this->post(self::ENDPOINT, [
            'file' => $this->createStationsExcel([
                [1, 'Avda. Primer Presidente', '-25.25595, -57.57174', 'Asunción', 'Capital', 'Propio', 'Grupo A', 'Shop', 'SI', 'NO'],
                [1, 'Avda. Primer Presidente', '-25.25595, -57.57174', 'Ciudad Falsa', 'Capital', 'Propio', 'Grupo A', 'Shop', 'SI', 'NO'],
            ]),
            'mapping' => $this->mapping(),
        ]);

        $response->assertOk();
        $response->assertJsonPath('skipped.0.row_data.direccion_yo_localidad', 'Avda. Primer Presidente');
        $response->assertJsonPath('skipped.0.row_data.coordenadas', '-25.25595, -57.57174');
        $response->assertJsonPath('skipped.0.row_data.ciudad', 'Ciudad Falsa');
    }

    /**
     * Reemplaza también el historial operativo de omitidas al iniciar un lote
     * nuevo para que el listado del admin no mezcle errores de importaciones
     * anteriores con los pendientes vigentes.
     */
    public function test_replaces_previous_skipped_records_when_a_new_import_runs(): void
    {
        [$admin] = $this->createAdminApiUser();
        Sanctum::actingAs($admin, ['admin']);
        $this->createCatalogCity('Asunción', 'Capital');

        DB::query()->from('estaciones_import_omitidas')->insert([
            'batch_id' => 'batch-anterior',
            'row_number' => 21,
            'direccion' => 'Registro omitido anterior',
            'coordenadas' => '-25.25595, -57.57174',
            'ciudad' => 'Ciudad vieja',
            'departamento' => 'Capital',
            'failed_field' => 'ciudad',
            'reason' => 'No se encontró una ciudad normalizada que coincida con la fila.',
            'status' => 'pendiente',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $response = $this->post(self::ENDPOINT, [
            'file' => $this->createStationsExcel([
                [1, 'Avda. válida', '-25.25595, -57.57174', 'Asunción', 'Capital', '', '', '', '', ''],
                [1, 'Avda. Primer Presidente', '-25.25595, -57.57174', 'Ciudad Falsa', 'Capital', '', '', '', '', ''],
            ]),
            'mapping' => $this->mapping(),
        ]);

        $response->assertOk();
        $this->assertDatabaseCount('estaciones_import_omitidas', 1);
        $this->assertDatabaseMissing('estaciones_import_omitidas', [
            'batch_id' => 'batch-anterior',
        ]);
        $this->assertDatabaseHas('estaciones_import_omitidas', [
            'direccion' => 'Avda. Primer Presidente',
            'ciudad' => 'Ciudad Falsa',
        ]);
    }

    /**
     * Permite cargar manualmente filas omitidas sin volver a reemplazar el lote
     * importado previamente.
     */
    public function test_imports_corrected_skipped_rows_without_replacing_existing_stations(): void
    {
        [$admin] = $this->createAdminApiUser();
        Sanctum::actingAs($admin, ['admin']);
        $firstCityId = $this->createCatalogCity('Asunción', 'Capital');
        $secondCityId = $this->createCatalogCity('Pedro Juan Caballero', 'Amambay');

        DB::query()->from('estaciones')->insert([
            'titulo' => '',
            'slug' => 'avda-primer-presidente',
            'estado' => true,
            'ciudad_id' => $firstCityId,
            'ubicacion' => json_encode(['lat' => -25.25595, 'lng' => -57.57174]),
            'direccion' => 'Avda. Primer Presidente',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $response = $this->postJson(self::SKIPPED_ENDPOINT, [
            'rows' => [
                [
                    'row' => 21,
                    'direccion' => 'Ruta 5, Km. 1',
                    'coordenadas' => '-22.56323, -55.71883',
                    'ciudad_id' => $secondCityId,
                    'ciudad' => 'Pedro Juan Caballero',
                    'departamento' => 'Amambay',
                ],
            ],
        ]);

        $response->assertOk();
        $response->assertJson([
            'imported_rows' => 1,
            'skipped_rows' => 0,
        ]);

        $this->assertDatabaseCount('estaciones', 2);
        $this->assertDatabaseHas('estaciones', [
            'direccion' => 'Ruta 5, Km. 1',
            'ciudad_id' => $secondCityId,
        ]);
    }

    /**
     * Asegura que la corrección manual siga validando el catálogo real.
     */
    public function test_rejects_unknown_city_ids_when_importing_corrected_rows(): void
    {
        [$admin] = $this->createAdminApiUser();
        Sanctum::actingAs($admin, ['admin']);

        $response = $this->postJson(self::SKIPPED_ENDPOINT, [
            'rows' => [
                [
                    'row' => 21,
                    'direccion' => 'Ruta 5, Km. 1',
                    'coordenadas' => '-22.56323, -55.71883',
                    'ciudad_id' => 999999,
                ],
            ],
        ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['rows.0.ciudad_id']);
    }

    /**
     * Catálogo mínimo de ciudad/departamento para el match del servicio.
     */
    private function createCatalogCity(string $city, string $department): int
    {
        $departmentId = DB::query()
            ->from('departamentos')
            ->where('nombre', $department)
            ->value('id');

        if ($departmentId === null) {
            $departmentId = DB::query()->from('departamentos')->insertGetId([
                'nombre' => $department,
                'slug' => str_slug($department),
                'estado' => true,
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        }

        return DB::query()->from('ciudades')->insertGetId([
            'nombre' => $city,
            'slug' => str_slug($city),
            'latitud' => -25.30,
            'longitud' => -57.60,
            'departamento_id' => $departmentId,
            'created_at' => now(),
            'updated_at' => now(),
        ]);
    }

    /**
     * Servicio mínimo para poblar la pivot y verificar su limpieza.
     */
    private function createService(): int
    {
        return DB::query()->from('servicios')->insertGetId([
            'titulo' => 'Market',
            'descripcion' => 'Servicio de prueba',
            'estado' => true,
            'color' => '#ff6600',
            'created_at' => now(),
            'updated_at' => now(),
        ]);
    }

    /**
     * Replica el mapping confirmado desde el modal del frontend.
     */
    private function mapping(): array
    {
        return [
            'direccion' => 'direccion_yo_localidad',
            'coordenadas' => 'coordenadas',
            'ciudad' => 'ciudad',
            'departamento' => 'departamento',
        ];
    }
}
