<?php

namespace App\Libraries;

/**
 * Subtrip ticket booking — segment overlap on the main trip stop list (ordered location IDs).
 *
 * Used when checking whether a seat is already sold for the requested pick/drop on the
 * same bus departure, including tickets booked under other subtrips on that trip.
 *
 * Rule: conflict when both journeys share a stop that is not a handoff
 * (one drops where the other picks at the same index → seat may be sold again).
 */
class SegmentSeatAvailability
{
    /**
     * @return list<string>
     */
    public static function parseStoppage(?string $stoppage): array
    {
        return array_values(array_filter(array_map('trim', explode(',', (string) $stoppage))));
    }

    public static function canResolveSegment(array $stops, $pickup, $drop): bool
    {
        return self::stopIndexOnRoute($stops, $pickup) !== null
            && self::stopIndexOnRoute($stops, $drop) !== null
            && self::stopIndexOnRoute($stops, $pickup) !== self::stopIndexOnRoute($stops, $drop);
    }

    /**
     * @param array<string,mixed> $row Ticket or temporarybook row joined with subtrip fields.
     * @return array{pick: string, drop: string}|null
     */
    public static function resolveBookingSegmentFromRow(array $stops, array $row, int $mainTripId = 0): ?array
    {
        $pickStandId = (int) ($row['pick_stand_id'] ?? 0);
        $dropStandId = (int) ($row['drop_stand_id'] ?? 0);

        if ($mainTripId > 0 && $pickStandId > 0 && $dropStandId > 0 && $stops !== []) {
            $fromStands = SegmentRouteTimeline::resolveLocationSegmentFromStands(
                $stops,
                $mainTripId,
                $pickStandId,
                $dropStandId
            );
            if ($fromStands !== null) {
                return $fromStands;
            }
        }

        $candidates = [];

        $ticketPick = (string) ($row['pick_location_id'] ?? '');
        $ticketDrop = (string) ($row['drop_location_id'] ?? '');
        if ($ticketPick !== '' && $ticketDrop !== '') {
            $candidates[] = [$ticketPick, $ticketDrop];
        }

        $subPick = (string) ($row['subtrip_pick_id'] ?? $row['pick_location_id'] ?? '');
        $subDrop = (string) ($row['subtrip_drop_id'] ?? $row['drop_location_id'] ?? '');
        if ($subPick !== '' && $subDrop !== '') {
            $candidates[] = [$subPick, $subDrop];
        }

        foreach ($candidates as [$pick, $drop]) {
            if (self::canResolveSegment($stops, $pick, $drop)) {
                return ['pick' => $pick, 'drop' => $drop];
            }
        }

        return null;
    }

    /**
     * Stand-aware overlap (preferred) with location-index fallback.
     */
    public static function bookingSegmentsOverlap(
        array $stops,
        int $mainTripId,
        $requestedPick,
        $requestedDrop,
        int $reqPickStandId,
        int $reqDropStandId,
        $existingPick,
        $existingDrop,
        int $exPickStandId,
        int $exDropStandId
    ): bool {
        if ($mainTripId > 0
            && $reqPickStandId > 0
            && $reqDropStandId > 0
            && $exPickStandId > 0
            && $exDropStandId > 0
            && SegmentRouteTimeline::segmentsOverlapByTravelOrder(
                $mainTripId,
                $reqPickStandId,
                $reqDropStandId,
                $exPickStandId,
                $exDropStandId
            )) {
            return true;
        }

        return self::segmentsOverlap($stops, $requestedPick, $requestedDrop, $existingPick, $existingDrop);
    }

    /**
     * @param array<int, string> $stops Main trip stoppage in route order.
     */
    public static function segmentsOverlap(
        array $stops,
        $requestedPick,
        $requestedDrop,
        $existingPick,
        $existingDrop
    ): bool {
        $reqPickIdx = self::stopIndexOnRoute($stops, $requestedPick);
        $reqDropIdx = self::stopIndexOnRoute($stops, $requestedDrop);
        $exPickIdx = self::stopIndexOnRoute($stops, $existingPick);
        $exDropIdx = self::stopIndexOnRoute($stops, $existingDrop);

        if ($reqPickIdx === null || $reqDropIdx === null || $exPickIdx === null || $exDropIdx === null) {
            return false;
        }

        if ($reqPickIdx === $reqDropIdx || $exPickIdx === $exDropIdx) {
            return false;
        }

        // A seat is considered occupied only when:
        // existing_pickup_order < requested_drop_order AND existing_drop_order > requested_pickup_order
        // We enforce that indices must follow the forward direction (pick < drop).
        // If they travel backwards, we'll assume it's invalid and return false, or we can use min/max if needed.
        // Assuming linear route:
        $reqStart = min($reqPickIdx, $reqDropIdx);
        $reqEnd = max($reqPickIdx, $reqDropIdx);
        $exStart = min($exPickIdx, $exDropIdx);
        $exEnd = max($exPickIdx, $exDropIdx);

        if ($exStart < $reqEnd && $exEnd > $reqStart) {
            return true;
        }

        return false;
    }

    public static function stopIndexOnRoute(array $stops, $locationId): ?int
    {
        foreach ($stops as $idx => $stopId) {
            if ((string) $stopId === (string) $locationId) {
                return (int) $idx;
            }
        }

        return null;
    }

    /**
     * @return list<int>
     */
    public static function traveledStopIndices(array $stops, $pick, $drop): array
    {
        $pickIdx = self::stopIndexOnRoute($stops, $pick);
        $dropIdx = self::stopIndexOnRoute($stops, $drop);
        if ($pickIdx === null || $dropIdx === null || $pickIdx === $dropIdx) {
            return [];
        }

        if ($pickIdx < $dropIdx) {
            return range($pickIdx, $dropIdx);
        }

        return range($dropIdx, $pickIdx);
    }
}
