<?php

namespace App\Libraries;

use Modules\Trip\Models\TripModel;
use Modules\Trip\Models\SubtripModel;
use Modules\Trip\Models\PickdropModel;

/**
 * Helper to compute the "bus start date" (main trip date) for a journey.
 *
 * IMPORTANT: pickdrops.trip_id stores subtrips.id (NOT trips.id).
 * We must first find the main subtrip (type='main') for the given trips.id,
 * then query pickdrops using that subtrip's id.
 */
class TripJourneyDateHelper
{
    protected $tripModel;
    protected $subtripModel;
    protected $pickdropModel;

    public function __construct()
    {
        $this->tripModel     = new TripModel();
        $this->subtripModel  = new SubtripModel();
        $this->pickdropModel = new PickdropModel();
    }

    /**
     * Get the bus start date (main trip date) for journeylist storage.
     *
     * @param int    $mainTripId      trips.id
     * @param int    $subtripId       subtrips.id (the segment being booked)
     * @param string $userJourneyDate User-selected boarding date (Y-m-d)
     * @param int    $pickStandId     pickdrops.id OR stand_id of the pickup point
     * @return string Bus start date in Y-m-d
     */
    public function getBusStartDateForJourneylist(int $mainTripId, int $subtripId, string $userJourneyDate, int $pickStandId): string
    {
        $userJourneyDate = date('Y-m-d', strtotime($userJourneyDate));
        $mainTrip        = $this->tripModel->find($mainTripId);

        if (!$mainTrip) {
            return $userJourneyDate;
        }

        $pickupDayOffset = $this->getPickupDayOffset($mainTrip, $mainTripId, $subtripId, $pickStandId);

        if ($pickupDayOffset === null) {
            return $userJourneyDate;
        }

        return date('Y-m-d', strtotime($userJourneyDate . " -{$pickupDayOffset} day"));
    }

    /**
     * Compute the passenger's actual boarding date from the normalized bus start date.
     * Boarding Date = Bus Start Date + offset of pickup stand.
     */
    public function getBoardingDateFromBusStartDate(int $mainTripId, int $subtripId, string $busStartDate, int $pickStandId): string
    {
        $busStartDate = date('Y-m-d', strtotime($busStartDate));
        $mainTrip     = $this->tripModel->find($mainTripId);

        if (!$mainTrip) {
            return $busStartDate;
        }

        $pickupDayOffset = $this->getPickupDayOffset($mainTrip, $mainTripId, $subtripId, $pickStandId);

        if ($pickupDayOffset === null) {
            return $busStartDate;
        }

        return date('Y-m-d', strtotime($busStartDate . " +{$pickupDayOffset} day"));
    }

    /**
     * Normalize time string to 24h format for comparison (e.g. "11:30PM" -> "23:30").
     */
    protected function normalizeTime(string $time): string
    {
        $time = trim($time);
        if (empty($time)) {
            return '00:00';
        }
        $timestamp = strtotime('1970-01-01 ' . $time);
        return $timestamp !== false ? date('H:i', $timestamp) : '00:00';
    }

    /**
     * Return how many days after the bus start date this pickup happens.
     *
     * Some trips do not keep active pickdrops on the main subtrip row. In that
     * case, use the actual selected subtrip pickup time to detect midnight
     * crossing.
     */
    protected function getPickupDayOffset(object $mainTrip, int $mainTripId, int $subtripId, int $pickStandId): ?int
    {
        $schedule          = (new \Modules\Schedule\Models\ScheduleModel())->find($mainTrip->schedule_id ?? 0);
        $mainTripStartTime = $schedule->start_time ?? '00:00';

        $mainPickdrops = $this->getMainSubtripPickdrops($mainTripId);
        if (!empty($mainPickdrops)) {
            $offset = $this->getOffsetFromPickdropSequence($mainPickdrops, $mainTripStartTime, $pickStandId);
            if ($offset !== null) {
                return $offset;
            }
        }

        return $this->getOffsetFromActualPickup($subtripId, $pickStandId, $mainTripStartTime);
    }

    protected function getMainSubtripPickdrops(int $mainTripId): array
    {
        $mainSubtrip = $this->subtripModel
            ->where('trip_id', $mainTripId)
            ->where('type', 'main')
            ->first();

        if (!$mainSubtrip) {
            return [];
        }

        return $this->pickdropModel
            ->select('pickdrops.id, pickdrops.stand_id, pickdrops.time, pickdrops.type')
            ->where('trip_id', (int) $mainSubtrip->id)
            ->orderBy('pickdrops.id', 'ASC')
            ->findAll();
    }

    protected function getOffsetFromPickdropSequence(array $pickdrops, string $mainTripStartTime, int $pickStandId): ?int
    {
        $targetStandId = $this->resolveTargetStandId($pickdrops, $pickStandId);
        if ($targetStandId === null) {
            return null;
        }

        $lastStopTime  = $mainTripStartTime;
        $currentOffset = 0;

        foreach ($pickdrops as $item) {
            $itemTime     = $this->normalizeTime($item->time ?? '00:00');
            $lastStopTime = $this->normalizeTime($lastStopTime);

            if (strtotime('1970-01-01 ' . $itemTime) < strtotime('1970-01-01 ' . $lastStopTime)) {
                $currentOffset++;
            }
            $lastStopTime = $itemTime;

            if ((int) $item->stand_id === $targetStandId) {
                return $currentOffset;
            }
        }

        return null;
    }

    protected function resolveTargetStandId(array $pickdrops, int $pickStandId): ?int
    {
        if ($pickStandId <= 0) {
            return null;
        }

        foreach ($pickdrops as $item) {
            if ((int) $item->id === $pickStandId) {
                return (int) $item->stand_id;
            }
        }

        $db       = \Config\Database::connect();
        $pdRecord = $db->table('pickdrops')->where('id', $pickStandId)->get()->getRow();
        if ($pdRecord) {
            return (int) $pdRecord->stand_id;
        }

        return $pickStandId;
    }

    protected function getOffsetFromActualPickup(int $subtripId, int $pickStandId, string $mainTripStartTime): ?int
    {
        if ($pickStandId <= 0) {
            return null;
        }

        $db       = \Config\Database::connect();
        $pickdrop = $db->table('pickdrops')
            ->select('id, stand_id, time, type')
            ->where('id', $pickStandId)
            ->get()
            ->getRow();

        if (!$pickdrop) {
            $pickdrop = $db->table('pickdrops')
                ->select('id, stand_id, time, type')
                ->where('trip_id', $subtripId)
                ->where('stand_id', $pickStandId)
                ->where('type', 1)
                ->orderBy('id', 'ASC')
                ->get()
                ->getRow();
        }

        if (!$pickdrop) {
            return null;
        }

        $pickupTime = $this->normalizeTime($pickdrop->time ?? '00:00');
        $startTime  = $this->normalizeTime($mainTripStartTime);

        return strtotime('1970-01-01 ' . $pickupTime) < strtotime('1970-01-01 ' . $startTime) ? 1 : 0;
    }
}
