<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use Carbon\Carbon;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\DealershipHoliday;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\VehicleReservations\FreeReservationExpiryCalculator;
use Tests\TenantTestCase;

/**
 * Covers the write path for the reservations.expires_at column: the value the app
 * actually derives when a free reservation is created (next working day at the
 * configured time, rolling forward over Sundays and dealership holidays).
 *
 * Weekday anchors used below (all 2026):
 *   Sat 01 Aug, Sun 02 Aug, Mon 03 Aug, Wed 05 Aug, Thu 06 Aug, Fri 07 Aug.
 */
class FreeReservationExpiryCalculatorTest extends TenantTestCase
{
    private function setSkipSundays(bool $value): void
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Sales',
            'group' => 'General',
            'name' => 'Free Reservations Do Not Expire On Sundays',
            'config_key' => 'sales-reservation-free-skip-sundays',
            'type' => 'boolean',
            'value' => $value,
        ]);
    }

    private function setExpiryTime(string $time): void
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Sales',
            'group' => 'General',
            'name' => 'Free Reservation Expiry Time',
            'config_key' => 'sales-reservation-free-expiry-time',
            'type' => 'string',
            'value' => $time,
        ]);
    }

    private function vehicleWithDealership(?Dealership $dealership): Vehicle
    {
        return Vehicle::factory()->create([
            'dealership_id' => $dealership?->id,
        ]);
    }

    public function testExpiresTheNextDayAtTheConfiguredTime()
    {
        $this->setExpiryTime('12:00');
        $vehicle = $this->vehicleWithDealership(Dealership::factory()->create());

        $expiry = (new FreeReservationExpiryCalculator())
            ->calculate($vehicle, Carbon::parse('2026-08-05 09:00'));

        $this->assertSame('2026-08-06 12:00:00', $expiry->toDateTimeString());
    }

    public function testHonoursTheConfiguredExpiryTime()
    {
        $this->setExpiryTime('17:30');
        $vehicle = $this->vehicleWithDealership(Dealership::factory()->create());

        $expiry = (new FreeReservationExpiryCalculator())
            ->calculate($vehicle, Carbon::parse('2026-08-05 09:00'));

        $this->assertSame('2026-08-06 17:30:00', $expiry->toDateTimeString());
    }

    public function testSkipsSundayWhenEnabled()
    {
        $this->setSkipSundays(true);
        $this->setExpiryTime('12:00');
        $vehicle = $this->vehicleWithDealership(Dealership::factory()->create());

        // Sat 01 Aug + 1 day = Sun 02 Aug -> rolls forward to Mon 03 Aug.
        $expiry = (new FreeReservationExpiryCalculator())
            ->calculate($vehicle, Carbon::parse('2026-08-01 09:00'));

        $this->assertSame('2026-08-03 12:00:00', $expiry->toDateTimeString());
    }

    public function testDoesNotSkipSundayWhenDisabled()
    {
        $this->setSkipSundays(false);
        $this->setExpiryTime('12:00');
        $vehicle = $this->vehicleWithDealership(Dealership::factory()->create());

        // Sat 01 Aug + 1 day = Sun 02 Aug, and Sundays are allowed.
        $expiry = (new FreeReservationExpiryCalculator())
            ->calculate($vehicle, Carbon::parse('2026-08-01 09:00'));

        $this->assertSame('2026-08-02 12:00:00', $expiry->toDateTimeString());
    }

    public function testSkipsADealershipHoliday()
    {
        $this->setExpiryTime('12:00');
        $dealership = Dealership::factory()->create();
        DealershipHoliday::query()->create([
            'dealership_id' => $dealership->id,
            'date' => '2026-08-06',
            'note' => 'Bank Holiday',
        ]);
        $vehicle = $this->vehicleWithDealership($dealership);

        // Wed 05 Aug + 1 day = Thu 06 Aug (holiday) -> rolls forward to Fri 07 Aug.
        $expiry = (new FreeReservationExpiryCalculator())
            ->calculate($vehicle, Carbon::parse('2026-08-05 09:00'));

        $this->assertSame('2026-08-07 12:00:00', $expiry->toDateTimeString());
    }

    public function testSkipsConsecutiveSundayAndHoliday()
    {
        $this->setSkipSundays(true);
        $this->setExpiryTime('12:00');
        $dealership = Dealership::factory()->create();
        DealershipHoliday::query()->create([
            'dealership_id' => $dealership->id,
            'date' => '2026-08-03',
            'note' => 'Bank Holiday',
        ]);
        $vehicle = $this->vehicleWithDealership($dealership);

        // Sat 01 Aug + 1 = Sun 02 (skip) -> Mon 03 (holiday, skip) -> Tue 04 Aug.
        $expiry = (new FreeReservationExpiryCalculator())
            ->calculate($vehicle, Carbon::parse('2026-08-01 09:00'));

        $this->assertSame('2026-08-04 12:00:00', $expiry->toDateTimeString());
    }

    public function testHandlesVehicleWithoutADealership()
    {
        $this->setExpiryTime('12:00');
        $vehicle = $this->vehicleWithDealership(null);

        // No dealership means no holidays to skip; still lands the next day.
        $expiry = (new FreeReservationExpiryCalculator())
            ->calculate($vehicle, Carbon::parse('2026-08-05 09:00'));

        $this->assertSame('2026-08-06 12:00:00', $expiry->toDateTimeString());
    }
}
