<?php

use App\Models\User;
use Livewire\Livewire;
use Modules\Catalog\Models\Destination;
use Modules\Catalog\Models\EvCompany;
use Modules\Routing\Filament\Resources\EvRoutes\Pages\CreateEvRoute;
use Modules\Routing\Filament\Resources\EvRoutes\Pages\EditEvRoute;
use Modules\Routing\Filament\Resources\EvRoutes\Pages\ListEvRoutes;
use Modules\Routing\Models\EvRoute;
use Modules\Routing\Models\RoutePricing;
use Modules\Shared\Enums\VehicleOption;
use Spatie\Permission\Models\Permission;

use function Pest\Laravel\assertDatabaseHas;

beforeEach(function () {
    Permission::findOrCreate('manage_routes', 'web');

    $this->admin = User::factory()->create()->givePermissionTo('manage_routes');
    $this->actingAs($this->admin);
});

function pricingPayload(array $overrides = []): array
{
    return collect(VehicleOption::cases())
        ->map(fn (VehicleOption $option) => array_merge([
            'vehicle_option' => $option->value,
            'price' => 0,
            'is_blocked' => false,
        ], $overrides[$option->value] ?? []))
        ->values()
        ->all();
}

test('can list ev routes', function () {
    $routes = EvRoute::factory()->count(3)->create();

    Livewire::test(ListEvRoutes::class)
        ->assertOk()
        ->assertCanSeeTableRecords($routes);
});

test('can filter ev routes by company, from, and to', function () {
    $companyA = EvCompany::factory()->create();
    $companyB = EvCompany::factory()->create();
    $yangon = Destination::factory()->create();
    $mandalay = Destination::factory()->create();
    $bagan = Destination::factory()->create();

    $matching = EvRoute::factory()->create([
        'ev_company_id' => $companyA->id,
        'from_destination_id' => $yangon->id,
        'to_destination_id' => $mandalay->id,
    ]);

    $wrongCompany = EvRoute::factory()->create([
        'ev_company_id' => $companyB->id,
        'from_destination_id' => $yangon->id,
        'to_destination_id' => $mandalay->id,
    ]);

    $wrongDestination = EvRoute::factory()->create([
        'ev_company_id' => $companyA->id,
        'from_destination_id' => $yangon->id,
        'to_destination_id' => $bagan->id,
    ]);

    Livewire::test(ListEvRoutes::class)
        ->filterTable('ev_company_id', $companyA->id)
        ->filterTable('from_destination_id', $yangon->id)
        ->filterTable('to_destination_id', $mandalay->id)
        ->assertCanSeeTableRecords([$matching])
        ->assertCanNotSeeTableRecords([$wrongCompany, $wrongDestination]);
});

test('list shows each vehicle option price stacked, and blocked options instead of a price', function () {
    $route = EvRoute::factory()->create();

    RoutePricing::factory()->create(['ev_route_id' => $route->id, 'vehicle_option' => VehicleOption::FrontSeat, 'price' => 12000]);
    RoutePricing::factory()->create(['ev_route_id' => $route->id, 'vehicle_option' => VehicleOption::BackSeat, 'price' => 9000]);
    RoutePricing::factory()->create(['ev_route_id' => $route->id, 'vehicle_option' => VehicleOption::WholeVehicle, 'is_blocked' => true]);

    Livewire::test(ListEvRoutes::class)
        ->assertOk()
        ->assertTableColumnStateSet('pricing', ['Back Seat: 9,000', 'Front Seat: 12,000', 'Whole Vehicle: Blocked'], $route);
});

test('creating a route also creates all three vehicle option pricing rows, defaulting to 0', function () {
    $company = EvCompany::factory()->create();
    $from = Destination::factory()->create();
    $to = Destination::factory()->create();

    Livewire::test(CreateEvRoute::class)
        ->fillForm([
            'ev_company_id' => $company->id,
            'from_destination_id' => $from->id,
            'to_destination_id' => $to->id,
            'is_active' => false,
            'pricing' => pricingPayload(),
        ])
        ->call('create')
        ->assertNotified()
        ->assertRedirect();

    $route = EvRoute::where('ev_company_id', $company->id)->firstOrFail();

    expect($route->pricing)->toHaveCount(3);

    foreach (VehicleOption::cases() as $option) {
        assertDatabaseHas(RoutePricing::class, [
            'ev_route_id' => $route->id,
            'vehicle_option' => $option->value,
            'price' => '0.00',
            'is_blocked' => false,
        ]);
    }
});

test('can price all three vehicle options directly on the create form', function () {
    $company = EvCompany::factory()->create();
    $from = Destination::factory()->create();
    $to = Destination::factory()->create();

    Livewire::test(CreateEvRoute::class)
        ->fillForm([
            'ev_company_id' => $company->id,
            'from_destination_id' => $from->id,
            'to_destination_id' => $to->id,
            'is_active' => true,
            'pricing' => pricingPayload([
                'front_seat' => ['price' => 12000],
                'back_seat' => ['price' => 9000],
                'whole_vehicle' => ['price' => 30000],
            ]),
        ])
        ->call('create')
        ->assertNotified()
        ->assertRedirect();

    $route = EvRoute::where('ev_company_id', $company->id)->firstOrFail();

    expect($route->is_active)->toBeTrue();

    assertDatabaseHas(RoutePricing::class, ['ev_route_id' => $route->id, 'vehicle_option' => 'front_seat', 'price' => 12000]);
    assertDatabaseHas(RoutePricing::class, ['ev_route_id' => $route->id, 'vehicle_option' => 'back_seat', 'price' => 9000]);
    assertDatabaseHas(RoutePricing::class, ['ev_route_id' => $route->id, 'vehicle_option' => 'whole_vehicle', 'price' => 30000]);
});

test('cannot create a route with the same from and to destination', function () {
    $company = EvCompany::factory()->create();
    $destination = Destination::factory()->create();

    Livewire::test(CreateEvRoute::class)
        ->fillForm([
            'ev_company_id' => $company->id,
            'from_destination_id' => $destination->id,
            'to_destination_id' => $destination->id,
            'pricing' => pricingPayload(),
        ])
        ->call('create')
        ->assertHasFormErrors(['to_destination_id' => 'different'])
        ->assertNotNotified()
        ->assertNoRedirect();

    $this->assertDatabaseCount(EvRoute::class, 0);
});

test('cannot create a route as active while any non-blocked vehicle option is priced at 0', function () {
    $company = EvCompany::factory()->create();
    $from = Destination::factory()->create();
    $to = Destination::factory()->create();

    Livewire::test(CreateEvRoute::class)
        ->fillForm([
            'ev_company_id' => $company->id,
            'from_destination_id' => $from->id,
            'to_destination_id' => $to->id,
            'is_active' => true,
            'pricing' => pricingPayload([
                'front_seat' => ['price' => 12000],
                // back_seat and whole_vehicle left at 0.
            ]),
        ])
        ->call('create')
        ->assertNotified();

    $this->assertDatabaseCount(EvRoute::class, 0);
});

test('a blocked vehicle option does not need a price to create an active route', function () {
    $company = EvCompany::factory()->create();
    $from = Destination::factory()->create();
    $to = Destination::factory()->create();

    Livewire::test(CreateEvRoute::class)
        ->fillForm([
            'ev_company_id' => $company->id,
            'from_destination_id' => $from->id,
            'to_destination_id' => $to->id,
            'is_active' => true,
            'pricing' => pricingPayload([
                'front_seat' => ['price' => 12000],
                'back_seat' => ['price' => 9000],
                'whole_vehicle' => ['is_blocked' => true],
            ]),
        ])
        ->call('create')
        ->assertNotified()
        ->assertRedirect();

    $route = EvRoute::where('ev_company_id', $company->id)->firstOrFail();

    expect($route->is_active)->toBeTrue();
});

test('can update pricing and toggle blocked from the edit page', function () {
    $route = EvRoute::factory()->create();

    foreach (VehicleOption::cases() as $option) {
        RoutePricing::factory()->create([
            'ev_route_id' => $route->id,
            'vehicle_option' => $option,
            'price' => 5000,
        ]);
    }

    Livewire::test(EditEvRoute::class, ['record' => $route->getRouteKey()])
        ->assertOk()
        ->fillForm([
            'pricing' => pricingPayload([
                'front_seat' => ['price' => 15000],
                'back_seat' => ['price' => 8000],
                'whole_vehicle' => ['is_blocked' => true],
            ]),
        ])
        ->call('save')
        ->assertNotified();

    assertDatabaseHas(RoutePricing::class, ['ev_route_id' => $route->id, 'vehicle_option' => 'front_seat', 'price' => 15000]);
    assertDatabaseHas(RoutePricing::class, ['ev_route_id' => $route->id, 'vehicle_option' => 'back_seat', 'price' => 8000]);
    assertDatabaseHas(RoutePricing::class, ['ev_route_id' => $route->id, 'vehicle_option' => 'whole_vehicle', 'is_blocked' => true]);

    expect($route->pricing()->count())->toBe(3);
});

test('cannot activate a route from the edit page while any non-blocked vehicle option is priced at 0', function () {
    $route = EvRoute::factory()->create(['is_active' => false]);
    foreach (VehicleOption::cases() as $option) {
        RoutePricing::factory()->create(['ev_route_id' => $route->id, 'vehicle_option' => $option]);
    }

    Livewire::test(EditEvRoute::class, ['record' => $route->getRouteKey()])
        ->assertOk()
        ->fillForm([
            'is_active' => true,
            'pricing' => pricingPayload([
                'front_seat' => ['price' => 12000],
                // back_seat and whole_vehicle left at 0.
            ]),
        ])
        ->call('save')
        ->assertNotified();

    expect($route->fresh()->is_active)->toBeFalse();
});
