<?php

require_once __DIR__ . '/SegmentSeatAvailabilityBootstrap.php';

use App\Libraries\EgyptTime;
use App\Libraries\TripJourneyDateHelper;
use PHPUnit\Framework\TestCase;

final class TripJourneyDateHelperStorageTest extends TestCase
{
    public function testDisplayReturnsStoredDateWhenTripUnknown(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertSame(
            '2026-07-20',
            $helper->getPassengerBoardingDateForDisplay(0, 0, '2026-07-20', 0)
        );
    }

    public function testJourneyListSearchMatchesBusStartOnly(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertTrue($helper->rowMatchesJourneyListSearchDate('2026-07-20', '2026-07-21', '2026-07-20'));
        $this->assertFalse($helper->rowMatchesJourneyListSearchDate('2026-07-20', '2026-07-21', '2026-07-21'));
    }

    public function testManifestSearchPrefetchesAdjacentDays(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertSame(
            ['2026-08-02', '2026-08-03'],
            $helper->getJourneyListPrefetchDates('2026-08-02')
        );
    }

    public function testResolveManifestFromJourneyListRowWithoutTripContextReturnsStored(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertSame(
            '2026-07-21',
            $helper->resolveManifestDateFromJourneyListRow(0, 0, '2026-07-21', 0)
        );
    }

    public function testTicketSearchIncludesAdjacentDaysForLegacyRows(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertSame(
            ['2026-07-20', '2026-07-19', '2026-07-21'],
            $helper->getTicketSearchMatchDates('2026-07-20')
        );
    }

    public function testManifestSearchWithoutTripContextUsesStoredListDateOnly(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertTrue($helper->rowMatchesJourneyListManifestSearch(
            0,
            0,
            '2026-07-20',
            0,
            '2026-07-20',
            '2026-07-21 00:00:00'
        ));
        $this->assertFalse($helper->rowMatchesJourneyListManifestSearch(
            0,
            0,
            '2026-07-20',
            0,
            '2026-07-21',
            '2026-07-21 00:00:00'
        ));
    }

    /** Ticket boarding day must not pull a row onto a different manifest search date. */
    public function testManifestSearchIgnoresTicketJourneyData(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertTrue($helper->rowMatchesJourneyListManifestSearch(
            0,
            0,
            '2026-08-02',
            0,
            '2026-08-02',
            '2026-08-03'
        ));
        $this->assertFalse($helper->rowMatchesJourneyListManifestSearch(
            0,
            0,
            '2026-08-02',
            0,
            '2026-08-03',
            '2026-08-03'
        ));
    }

    public function testTripSearchDateIsLiteralBusRunDay(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertSame(
            '2026-08-03',
            $helper->resolveBusRunDateForTripSearch('2026-08-03')
        );
    }

    public function testTicketStorageUsesBusStartWhenTripUnknown(): void
    {
        $helper = new TripJourneyDateHelper();

        $this->assertSame(
            '2026-08-02',
            $helper->getTicketJourneyDateForStorage(0, 0, '2026-08-02', 0)
        );
    }

    /** journeylists bus run day wins over tickets.journeydata when both agree on manifest date. */
    public function testManifestSearchTrustsStoredBusRunDay(): void
    {
        $helper = $this->midnightTripHelperMock();

        $this->assertTrue($helper->rowMatchesJourneyListManifestSearch(
            75,
            238,
            '2026-06-18',
            8183,
            '2026-06-18',
            '2026-06-18'
        ));
    }

    /** Ticket boarding day must not duplicate the same run onto the next manifest search date. */
    public function testOvernightRunAppearsOnOneManifestDateOnly(): void
    {
        $helper = $this->midnightTripHelperMock();

        $this->assertTrue($helper->rowMatchesJourneyListManifestSearch(
            75,
            238,
            '2026-06-15',
            8182,
            '2026-06-15',
            '2026-06-16'
        ));
        $this->assertFalse($helper->rowMatchesJourneyListManifestSearch(
            75,
            238,
            '2026-06-15',
            8182,
            '2026-06-16',
            '2026-06-16'
        ));
    }

    public function testBusRunInputIsNotTreatedAsBoardingDay(): void
    {
        $helper = $this->midnightTripHelperMock();

        $this->assertSame(
            '2026-06-15',
            $helper->getManifestDateForJourneylist(75, 238, '2026-06-15', 8182)
        );
        $this->assertSame(
            '2026-06-15',
            $helper->boardingDayToBusRunDate(75, 238, '2026-06-16', 8182)
        );
        $this->assertSame(
            '2026-06-16',
            $helper->getTicketJourneyDateForStorage(75, 238, '2026-06-15', 8182)
        );

        $this->assertSame(
            '2026-06-15',
            $helper->boardingDayToBusRunDate(75, 238, '2026-06-16', 8182)
        );

        $this->assertSame(
            '2026-06-15',
            $helper->resolveBusRunDayFromTicketJourneyData(75, 238, '2026-06-16', 8182)
        );
    }

    /** @return TripJourneyDateHelper */
    private function midnightTripHelperMock()
    {
        return new class extends TripJourneyDateHelper {
            protected function getPickupDayOffset(object $mainTrip, int $mainTripId, int $subtripId, int $pickStandId): ?int
            {
                return 1;
            }

            public function getBoardingDateFromBusStartDate(int $mainTripId, int $subtripId, string $busStartDate, int $pickStandId): string
            {
                return EgyptTime::toDateString($busStartDate . ' +1 day');
            }

            protected function getTrip(int $mainTripId): ?object
            {
                return (object) ['schedule_id' => 1];
            }
        };
    }

}
