<?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): ?array
    {
        $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;
    }

    /**
     * @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;
        }

        // Occupied only when intervals overlap on the route axis (handoff at the same stop is allowed).
        // Use directional indices — do not collapse with min/max or Ras Sudr→El Tor falsely overlaps Cairo→Ras Sudr.
        // Normalize existing bookings stored with pick/drop reversed on the main stop list.
        if ($exPickIdx > $exDropIdx) {
            [$exPickIdx, $exDropIdx] = [$exDropIdx, $exPickIdx];
        }

        return $exPickIdx < $reqDropIdx && $exDropIdx > $reqPickIdx;
    }

    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);
    }
}
