<?php

namespace App\Http\Controllers\Client\Package;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Helpers\ResponseBuilder;
use App\Modules\HtlCamp\Application\HtlCampIndexer;
use App\Modules\HtlCamp\Domain\IdHtlCamp;
use App\Modules\HtlCamp\Infraestructure\MysqlHtlCampRepository;
use App\Modules\HtlCampSchedule\Application\List\ListHtlCampScheduleQuery;
use App\Modules\HtlCampSchedule\Application\Order\CalendarGenerator;
use App\Modules\HtlCampSchedule\Application\Order\MapSchedule;
use App\Modules\HtlCampSchedule\Application\Order\OrderHtlCampScheduleQuery;
use App\Modules\Package\Infraestructure\PackageValidators;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\Shared\Domain\DateEnd;
use App\Modules\Shared\Domain\DateStart;
use App\Modules\Shared\Domain\Transaction;
use App\Modules\Shared\Exceptions\BasicRequestException;
use App\Modules\Shared\Validator\Infraestructure\LaravelValidator;
use App\Modules\Tour\Domain\TourAvailabilityStatus;
use DateInterval;
use DateTime;
use Illuminate\Support\Facades\DB;

class PackageAvailabilityListController extends Controller
{
    private $domain;
    private TourCalendarList $tourCalendarList;
    private CampCalendarList $campCalendarList;
    private TourLockList $tourLockList;

    public function __construct(
        private QueryBus $queryBus,
        private CalendarGenerator $generator,
        private CalendarRepository $calendarRepository,
        private Transaction $transaction
    ) {
        $this->domain = "Package";
    }

    public function __invoke(Request $request)
    {
        $validators = PackageValidators::AVAILABILITY;
        (new LaravelValidator())->validate($request->all(), $this->domain, $validators);
        $month = $request->input('Month');
        $year = $request->input('Year');
        $dateStart = new DateStart($year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01');
        $dateEnd = new DateEnd($year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01');
        $dateEnd->addInterval('P1M');
        $dateEnd->subInterval('P1D');
        $this->tourCalendarList = new TourCalendarList();
        $this->campCalendarList = new CampCalendarList();
        $this->tourLockList = new TourLockList();
        // $dateStart = new DateStart($request->input('Date_Start'));
        // $dateEnd = new DateEnd($request->input('Date_End'));
        $calendar = new PackageCalendar($dateStart, $dateEnd);

        $idPackage = $request->input('Id_Package');

        $this->transaction->begin();
        $package = $this->getPackageIndex($idPackage);
        $tourList = $this->getTourList($package->Id_Package);
        $days = $this->getInitialAndLastDay($tourList);
        $dateStart->addInterval('P' . ($days['InitialDay'] - 1) . 'D');
        $dateEnd->addInterval('P' . ($days['LastDay'] - 1) . 'D');

        $this->fillCalendars($tourList, $dateStart, $dateEnd);
        $days = [];
        foreach ($calendar->days() as $day) {
            $day->lockRemark = '';
            $firstTour = true;

            // ------------------------------------------------------------------------------------------------------------------------------------
            // 20/08/2025
            // agregar el campo debug_backtrace min de pasajeros disponobles comparar los elementos de $tourList con $this->tourCalendarList y 
            // si tiene esa fecha agarrar el pax y poner el mínimo de pax
            // fecha del dia: $day->date
            // tours del paquete: $tourList
            // tours en un tango de fechas: $this->tourCalendarList
            // dd($this->tourCalendarList);
            $minToursPax = null;
            $paxByTour = [];
            $baseDate = $day->date;
            $accumulatedDays = 0;
            $allCamps = [];

            foreach ($tourList as $tour) {
                // --------------------------------llenar los pax minimos por tour acumulados por tour--------------------------------
                $tourId = $tour->Id_Tour;
                $tourDuration = (int) $tour->Tour_Duration;
                // Sumar días a la fecha inicIal
                $tourDateStr = date('Y-m-d', strtotime("+{$accumulatedDays} days", strtotime($baseDate)));
                $tourDateObj = new DateTime($tourDateStr);

                $pax = 0;
                if ($this->tourCalendarList->has($tourId)) {
                    $mapSchedule = $this->tourCalendarList->get($tourId);
                    $daySchedule = $mapSchedule->getDate($tourDateObj);
                    if ($daySchedule !== null) {
                        $pax = $daySchedule->pax ?? 0;
                    }
                }
                $paxByTour[$tourId] = $pax;
                $minToursPax = is_null($minToursPax) ? $pax : min($minToursPax, $pax);
                $accumulatedDays += $tourDuration;


                // -----------------------------------llenar los campamentos por tour------------------------------
            }
            // dd($paxByTour); // se guarda todos los pax por tour
            // se guarda el minimo de pasajeros por tour
            $day->minToursPackagePax = $minToursPax;
            // se guardan todos los campamentos
            $day->camps = $allCamps;
            // -------------------------------------------------------------------------------------------------------------
            foreach ($tourList as $i => $tour) {
                $date = DateTime::createFromFormat('Y-m-d H:i:s', $day->date . ' 00:00:00');
                $date->add(new DateInterval('P' . ($tour->day - 1) . 'D'));

                $dateElement = $this->tourCalendarList->get($tour->Id_Tour)->getDate($date);
                if ($tour->Tour_AvailabilityStatus === TourAvailabilityStatus::YES) {
                    $day->availability = $this->appendAvailability($day, $tour->Tour_Availability);
                }

                $day->paxMin = max($day->paxMin, $tour->Tour_MinPax);

                $day->lock = $day->lock || $this->tourLockList->get($tour->Id_Tour)->isLock($date);

                if (!$dateElement) {
                    $dateElement = (object) [
                        'pax' => 0,
                    ];
                }
                if ($i == 0) {
                    $day->paxAmount = $dateElement->pax;
                } else {
                    $day->paxAmount = min($day->paxAmount, $dateElement->pax);
                }
                if ($tour->Tour_AvailabilityStatus === TourAvailabilityStatus::YES) {
                    $day->paxFree = $firstTour
                        ? $tour->Tour_Availability - $dateElement->pax
                        : min($day->paxFree, $tour->Tour_Availability - $dateElement->pax);
                    $firstTour = false;
                    $day->availabilityStatus = true;
                }

                // ------------------------------------------------------------------------------------------
                foreach ($tour->campList as $campIndex => $camp) {

                    $date = DateTime::createFromFormat('Y-m-d', $day->date);
                    $date->add(new DateInterval('P' . ($camp->day - 1) . 'D'));
                    $campElement = $this->campCalendarList->get($camp->Id_HtlCamp)->getDate($date);

                    $schedule = count($campElement->schedule) ? $campElement->schedule[0] : (object) ['HtlCampSchedule_Lock' => 1, 'HtlCampSchedule_AmountUsage' => 0];
                    $day->lock = $day->lock || $schedule->HtlCampSchedule_Lock == 2;

                    // aquí están todos los campamentos
                    // dd($this->appendAvailability($day, $campElement->availability));
                    $day->availability = $this->appendAvailability($day, $campElement->availability);


                    if (!$day->availabilityStatus) {
                        if (count($campElement->schedule)) {
                            // dd($campElement);
                            // dd(json_decode($schedule->HtlCampBooking, true));
                            $day->paxFree = $campElement->availability - $schedule->HtlCampSchedule_AmountUsage;
                        } else {
                            $day->paxFree = $campElement->availability;
                        }
                    } else {
                        if (count($campElement->schedule)) {
                            $day->paxFree = min($day->paxFree, $campElement->availability - $schedule->HtlCampSchedule_AmountUsage);
                        } else {
                            $day->paxFree = min($day->paxFree, $campElement->availability - 0);
                        }
                    }
                    $day->availabilityStatus = true;
                }

                //nuevo campo para la cantidad mínima por día
                $day->paxMinByPackage = $day->paxMin - $day->minToursPackagePax;
            }
            $days[] = $day;
        }

        $this->transaction->commit();

        return ResponseBuilder::Response([
            "Response_Status"           => 200,
            "Response_Code"             => 200,
            "Response_Data"             => $days,
            "Response_Domain"           => $this->domain,
            "Response_Message"          => 'Lista',
            "Response_Error_Message"    => '',
            "Response_Error_Reason"     => '',
        ]);
    }

    private function appendAvailability($day, $availability)
    {
        return $day->availability === -1
            ? $availability
            : min($day->availability, $availability);
    }

    private function fillCalendars($tourList, DateStart $dateStart, DateEnd $dateEnd)
    {
        foreach ($tourList as $tour) {
            if (!$this->tourCalendarList->has($tour->Id_Tour)) {
                $this->tourCalendarList->set(
                    $tour->Id_Tour,
                    $this->calendarRepository->findTourCalendar($tour->Id_Tour, $dateStart->value(), $dateEnd->value())
                );
            }
            if (!$this->tourLockList->has($tour->Id_Tour)) {
                $this->tourLockList->set(
                    $tour->Id_Tour,
                    new TourLockCalendar($this->calendarRepository->findTourLock(
                        $tour->Id_Tour,
                        $dateStart->value(),
                        $dateEnd->value()
                    ))
                );
            }

            foreach ($tour->campList as $camp) {
                if (!$this->campCalendarList->has($camp->Id_HtlCamp)) {
                    $this->campCalendarList->set(
                        $camp->Id_HtlCamp,
                        $this->calendarCamp($camp, $dateStart->value(), $dateEnd->value())
                    );
                }
            }
        }
    }

    private function calendarCamp($camp, $dateStart, $dateEnd)
    {
        $indexResponse = (new HtlCampIndexer(new MysqlHtlCampRepository))->__invoke(new IdHtlCamp($camp->Id_HtlCamp));
        /** @var HtlCampScheduleResponses $response */
        $response = $this->queryBus->ask(new ListHtlCampScheduleQuery($camp->Id_HtlCamp, $dateStart . ' 00:00:00', $dateEnd . ' 23:59:59'));
        /** @var OrderHtlCampScheduleResponse $order */
        $order = $this->queryBus->ask(new OrderHtlCampScheduleQuery(
            $dateStart . ' 00:00:00',
            $dateEnd . ' 23:59:59',
            $response->data(),
            $indexResponse['Response_Data'][0],
            'external'
        ));
        foreach ($order->response as $day) {
            $day->date = date('Y-m-d', strtotime($day->date));
        }
        return new MapSchedule($order->response);
    }

    private function getInitialAndLastDay($tourList)
    {
        $initialDay = 1;
        $lastDay = 1;
        foreach ($tourList as $i => $tour) {
            if ($i !== 0) {
                $lastDay = $tour->day > $lastDay ? $tour->day : $lastDay;
            }

            foreach ($tour->campList as $camp) {
                $day = $camp->TourHtlCamp_Day + $tour->day - 1;
                $lastDay = $day > $lastDay ? $day : $lastDay;
                $initialDay = $day < $initialDay ? $day : $initialDay;
            }
        }

        return ['InitialDay' => $initialDay, 'LastDay' => $lastDay];
    }

    private function getPackageIndex($idPackage)
    {
        $package = DB::select(
            'SELECT  `Id_Package`,
                `Package_Code`,
                `Package_Name`,
                `Package_UrlThumbnail`,
                `Package_Duration`,
                `Package_Availability`,
                `Package_PaxRequired`,
                `Package_DOBRequired`,
                `Package_Type`,
                `Package_Status`,
                `Package_Endorser`
            FROM    `t_package` 
            WHERE   `Id_Package` = ?',
            [$idPackage]
        );
        if (!count($package)) {
            throw new BasicRequestException(404, 404, 'No existe el paquete');
        }
        return $package[0];
    }

    private function getTourList($idPackage)
    {
        $tourList = DB::select(
            'SELECT  `pt`.`Id_PackageTour`,
                `pt`.`PackageTour_Order`,
                `pt`.`Id_Package`,
                `t`.`Id_Tour`,
                `t`.`Tour_Code`,
                `t`.`Tour_Name`,
                `t`.`Tour_Duration`,
                `t`.`Tour_MinPax`,
                `t`.`Tour_Availability`,
                `t`.`Tour_AvailabilityStatus`
            FROM    `t_package_tour`    AS `pt` INNER JOIN 
                    `t_tour`            AS `t` ON `t`.`Id_Tour` = `pt`.`Id_Tour`
            WHERE   `pt`.`Id_Package` = ?
            ORDER BY `PackageTour_Order` ASC',
            [$idPackage]
        );

        foreach ($tourList as $i => $tour) {
            $tour->campList = $this->getTourHtlCampList($tour->Id_Tour);
            $tour->day = $i === 0 ? 1 : $tourList[$i - 1]->day + $tourList[$i - 1]->Tour_Duration;
            foreach ($tour->campList as $camp) {
                $camp->day = $tour->day + $camp->TourHtlCamp_Day - 1;
            }
        }
        return $tourList;
    }

    private function getTourHtlCampList($idTour)
    {
        return DB::select(
            "SELECT  `thc`.`Id_TourHtlCamp`,
                    `thc`.`TourHtlCamp_Day`,
                    `thc`.`TourHtlCamp_Status`,
                    `thc`.`Id_Tour`,
                    `hc`.`Id_HtlCamp`,
                    `hc`.`HtlCamp_Name`,
                    `hc`.`HtlCamp_Abrv`,
                    `hc`.`HtlCamp_Availability`,
                    `hc`.`HtlCamp_Status`
            FROM  `t_tour_htl_camp` AS `thc` INNER JOin
                `t_htl_camp` AS `hc` ON `hc`.`Id_HtlCamp` = `thc`.`Id_HtlCamp`
            WHERE
            CASE
                WHEN ? = 'active' THEN `TourHtlCamp_Status` = 2
                WHEN ? = 'all' THEN `TourHtlCamp_Status` > 0
                WHEN ? = 'deleted' THEN `TourHtlCamp_Status` = 0
            END
            AND
            `thc`.`Id_Tour` = ?
            ORDER BY `thc`.`TourHtlCamp_Day`",
            ['active', 'active', 'active', $idTour]
        );
    }
}
