<?php

require_once __DIR__ . '/SegmentSeatAvailabilityBootstrap.php';

use App\Libraries\SegmentSeatAvailability;
use PHPUnit\Framework\TestCase;

/**
 * Route A→B→C→D→E→F (indices 0..5). Location IDs are letters for readability.
 *
 * @internal
 */
final class SegmentSeatAvailabilityTest extends TestCase
{
    /** @var list<string> */
    private array $stops = ['A', 'B', 'C', 'D', 'E', 'F'];

    public function testNoOverlapWhenSegmentsOnlyTouchAtHandoff(): void
    {
        $this->assertFalse(SegmentSeatAvailability::segmentsOverlap($this->stops, 'A', 'C', 'C', 'E'));
        $this->assertFalse(SegmentSeatAvailability::segmentsOverlap($this->stops, 'C', 'E', 'A', 'C'));
    }

    public function testNoOverlapAdjacentLegs(): void
    {
        $this->assertFalse(SegmentSeatAvailability::segmentsOverlap($this->stops, 'B', 'E', 'A', 'B'));
    }

    public function testOverlapWhenInnerSegmentInsideOuter(): void
    {
        $this->assertTrue(SegmentSeatAvailability::segmentsOverlap($this->stops, 'A', 'D', 'B', 'C'));
    }

    public function testOverlapWhenLongerSegmentContainsShorter(): void
    {
        $this->assertTrue(SegmentSeatAvailability::segmentsOverlap($this->stops, 'B', 'E', 'A', 'D'));
        $this->assertTrue(SegmentSeatAvailability::segmentsOverlap($this->stops, 'B', 'E', 'B', 'E'));
    }

    public function testNoOverlapWhenOnlyShareDropOfFirstWithPickOfSecond(): void
    {
        $this->assertFalse(SegmentSeatAvailability::segmentsOverlap($this->stops, 'B', 'E', 'A', 'B'));
    }

    public function testUnknownStopsDoNotOverlap(): void
    {
        $this->assertFalse(SegmentSeatAvailability::segmentsOverlap($this->stops, 'B', 'E', 'X', 'Y'));
        $this->assertFalse(SegmentSeatAvailability::segmentsOverlap($this->stops, 'B', 'B', 'A', 'C'));
    }

    public function testResolveBookingSegmentPrefersTicketPickDrop(): void
    {
        $segment = SegmentSeatAvailability::resolveBookingSegmentFromRow($this->stops, [
            'pick_location_id' => 'B',
            'drop_location_id' => 'D',
            'subtrip_pick_id' => 'A',
            'subtrip_drop_id' => 'F',
        ]);
        $this->assertSame(['pick' => 'B', 'drop' => 'D'], $segment);
    }

    public function testParseStoppageTrimsAndFilters(): void
    {
        $this->assertSame(['55', '41', '43'], SegmentSeatAvailability::parseStoppage(' 55, 41 ,,43 '));
    }

    /** Trip 75-style main stoppage: Cairo, Sharm, El Tor, Ras Sudr, … */
    public function testNoOverlapWhenLaterLegStartsWhereEarlierBookingEnds(): void
    {
        $stops = ['32', '41', '43', '54', '65', '66'];

        $this->assertFalse(SegmentSeatAvailability::segmentsOverlap($stops, '54', '43', '32', '54'));
    }

    public function testOverlapWhenExistingBookingStoredWithReversedPickDrop(): void
    {
        $stops = ['32', '41', '43', '54'];

        $this->assertTrue(SegmentSeatAvailability::segmentsOverlap($stops, '32', '41', '41', '32'));
    }

    public function testOverlapForNestedSegmentOnSameRoute(): void
    {
        $stops = ['32', '41', '43', '54'];

        $this->assertTrue(SegmentSeatAvailability::segmentsOverlap($stops, '32', '41', '32', '43'));
    }

    /** Trip 75: Cairo→Sharm subtrip passes Ras Sudr / El Tor; blocks Ras Sudr→El Tor on same bus. */
    public function testPathOverlapCairoSharmBlocksRasSudrToElTor(): void
    {
        $stops = ['32', '41', '43', '54', '65', '66', '67', '77', '78', '90', '55'];
        $cairoSharmStoppage = '32,43,54,66,67,78,41';
        $rasSudrTorStoppage = '54,43';

        $this->assertTrue(SegmentSeatAvailability::segmentsOverlapWithPath(
            $stops,
            '32',
            '41',
            $cairoSharmStoppage,
            '54',
            '43',
            $rasSudrTorStoppage
        ));
    }

    /** Trip 75: Cairo→Ras Sudr hands off at Ras Sudr; does not block Ras Sudr→El Tor. */
    public function testPathNoOverlapCairoRasSudrVsRasSudrToElTor(): void
    {
        $stops = ['32', '41', '43', '54', '65', '66'];
        $cairoRasSudrStoppage = '32,43,54';

        $this->assertFalse(SegmentSeatAvailability::segmentsOverlapWithPath(
            $stops,
            '32',
            '54',
            $cairoRasSudrStoppage,
            '54',
            '43',
            '54,43'
        ));
    }

    /** Trip 75: identical Ras Sudr→El Tor segment must block the same seats. */
    public function testTrip75SameRasSudrToElTorSegmentOverlaps(): void
    {
        $stops = ['32', '41', '43', '54', '65', '66', '67', '77', '78', '90', '55'];

        $this->assertTrue(SegmentSeatAvailability::segmentsOverlapWithPath(
            $stops,
            '54',
            '43',
            '54,43',
            '54',
            '43',
            '54,43'
        ));
    }

    /** Trip 75 search Ras Sudr→El Tor: all booked seats on that bus departure must conflict. */
    public function testTrip75RasSudrSearchBlocksAllScenarioSeats(): void
    {
        $stops = ['32', '41', '43', '54', '65', '66', '67', '77', '78', '90', '55'];
        $requestStoppage = '54,43';

        $bookings = [
            ['54', '43', '54,43'],
            ['54', '43', '54,43'],
            ['32', '41', '32,43,54,66,67,78,41'],
        ];

        $blocked = [];
        foreach ($bookings as [$pick, $drop, $stoppage]) {
            if (SegmentSeatAvailability::segmentsOverlapWithPath(
                $stops,
                '54',
                '43',
                $requestStoppage,
                $pick,
                $drop,
                $stoppage
            )) {
                $blocked[] = true;
            }
        }

        $this->assertCount(3, $blocked);
    }
}
