<?php

namespace App\Http\Controllers\Client;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Owner\Passenger\PassengerUpdater;
use App\Modules\Shared\Application\SelectHandler;
use Illuminate\Http\Request;
use App\Http\Helpers\ResponseBuilder;
use App\Http\Helpers\ParamsValidator;
use App\Http\History\HistoryData;
use App\Http\History\HistoryTables;
use App\Modules\Shared\Domain\ValueObject\DateValueObject;
use App\Providers\BookingMailSender;
use App\Providers\PackageAvailability\InvoiceAnalizerProvider;
use App\Providers\PackageAvailability\PackageAvailabilityProvider;
use App\Providers\PackageAvailability\TourBooking\BookingNotAvailable;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class c_Schedule extends Controller
{
    private $oDomain;
    private HistoryData $history;

    public function __construct(
        private InvoiceAnalizerProvider $invoiceAnalyzer,
        private PackageAvailabilityProvider $packageAvailability,
        private PassengerUpdater $passengerUpdater,
    ) {
        $this->oDomain = "Schedule";
        $this->history = new HistoryData(HistoryTables::SCHEDULE);
    }

    public static function findBooking($idSchedule)
    {
        $found = DB::select('CALL sp_schedule_index(?)', [$idSchedule]);

        if (count($found)) {
            return $found[0]->Id_Booking;
        } else {
            return null;
        }
    }

    public function Pay(Request $Request)
    {
        $oResponse  = array();
        $oValParams = [
            'Id_Schedule'           => 'required|int',
            'CashMovement'          => 'required|string'
        ];

        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Request($Request, $oValParams, $this->oDomain);

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $oCashMovement  = json_decode($Request->input('CashMovement'));

            $oParam = [
                DateValueObject::reducedNowToPeru(),
                $oCashMovement->CashMovement_Amount,
                $oCashMovement->CashMovement_Currency,
                $oCashMovement->CashMovement_Name,
                $oCashMovement->CashMovement_LastName,
                $oCashMovement->CashMovement_Email,
                $oCashMovement->CashMovement_Country,
                $oCashMovement->CashMovement_City,
                $oCashMovement->CashMovement_Address,
                $oCashMovement->CashMovement_Phone,
                $oCashMovement->CashMovement_ReceiptNumber,
                $oCashMovement->CashMovement_CardLast,
                $oCashMovement->CashMovement_CardNumber,
                $oCashMovement->CashMovement_CardBrand,
                $oCashMovement->CashMovement_CardType,
                $oCashMovement->CashMovement_CardIssuer,
                '',
                14
            ];

            $oDataCashMovement = DB::select('call sp_cash_movement_insert(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)', $oParam);
            $oData = $oDataCashMovement;


            if ($oData[0]->Response_Success == 1) {
                // $oScheduleDetail = DB::select ('call sp_schedule_detail_list(?)', [ $Request->input ('Id_Schedule') ]);
                $invoiceDetail = json_decode($oCashMovement->Invoices);

                foreach ($invoiceDetail as $detail) {
                    $oDataInvoice = DB::select(
                        'call sp_cash_movement_detail_insert(?,?,?,?,?)',
                        [
                            $detail->Amount,
                            $oDataCashMovement[0]->Id_CashMovement,
                            $detail->Id_Invoice,
                            1,
                            1
                        ]
                    );
                    $oData = $oDataInvoice;
                    if ($oData[0]->Response_Success != 1) {
                        break;
                    }
                }
                if ($oData[0]->Response_Success == 1) {
                    $this->updateLockByInvoices($invoiceDetail);

                    $dataPayment = DB::select('call sp_schedule_pay(?)', [$Request->input("Id_Schedule")]);

                    if ($dataPayment[0]->Response_Success == 1) {
                        try {
                            $oSchedule = DB::select('call sp_schedule_index(?)', [$Request->input("Id_Schedule")]);
                            BookingMailSender::sendBookingByMail(
                                $oSchedule[0]->Id_Booking,
                                $this->oDomain,
                                BookingMailSender::ONLY_PAYMENT
                            );
                        } catch (\Throwable $ex) {
                        }
                    }
                }
            }

            $oResponse["Response_Status"]           = $oData[0]->Response_Status;
            $oResponse["Response_Code"]             = $oData[0]->Response_Code;
            $oResponse["Response_Domain"]           = $this->oDomain;
            $oResponse["Response_Message"]          = $oData[0]->Response_Message;
            $oResponse["Response_Data"]             = null;
            $oResponse["Response_Error_Message"]    = $oData[0]->Response_Message;
            $oResponse["Response_Error_Reason"]     = $oData[0]->Response_Reason;
        }

        return ResponseBuilder::Response($oResponse);
    }

    private function updateLockByInvoices($invoiceDetail)
    {
        try {
            $updated = [];
            foreach ($invoiceDetail as $detail) {
                try {
                    $idInvoice = $detail->Id_Invoice ?? null;
                    if ($idInvoice === null) {
                        continue;
                    }
                    $invoice = (new SelectHandler())->handle(DB::select('CALL sp_invoice_get(?)', [$idInvoice]));

                    if (!count($invoice) || empty($invoice[0]->Id_Passenger)) {
                        // Anómalo: el invoice acaba de recibir el pago; debería resolver a un pasajero.
                        Log::warning('c_Schedule::updateLockByInvoices - invoice pagado sin pasajero resoluble', [
                            'Id_Invoice' => $idInvoice,
                        ]);
                        continue;
                    }

                    $idPassenger = $invoice[0]->Id_Passenger;
                    if (in_array($idPassenger, $updated)) {
                        continue;
                    }

                    $updated[] = $idPassenger;
                    $this->passengerUpdater->updateLockHtlCampBookingPassenger($idPassenger);
                } catch (\Throwable $ex) {
                    Log::error('c_Schedule::updateLockByInvoices - fallo por invoice', [
                        'Id_Invoice' => $detail->Id_Invoice ?? null,
                        'error'      => $ex->getMessage(),
                    ]);
                }
            }
        } catch (\Throwable $ex) {
            Log::error('c_Schedule::updateLockByInvoices - fallo general', [
                'error' => $ex->getMessage(),
            ]);
        }
    }

    public function Search(Request $Request)
    {

        // VALIDANDO DATOS
        $oResponse = ParamsValidator::Validate_Request(
            $Request,
            ['Schedule_Token'    => 'string'],
            $this->oDomain
        );
        $Schedule_Token = $Request->input('Schedule_Token', '');

        //  INGRESANDO DATOS
        if ($oResponse["Response_Code"] == 200) {
            $oParam = [$Schedule_Token];
            $oData = DB::select('call sp_schedule_search (?)', $oParam);
            $oData = array_map([$this, 'richSchedule'], $oData);

            $oResponse["Response_Status"]           = 200;
            $oResponse["Response_Code"]             = 200;
            $oResponse["Response_Domain"]           = $this->oDomain;
            $oResponse["Response_Message"]          = $this->oDomain . " Index";
            $oResponse["Response_Data"]             = $oData;
            $oResponse["Response_Error_Message"]    = "";
            $oResponse["Response_Error_Reason"]     = "";
        }

        return ResponseBuilder::Response($oResponse);
    }

    private function richSchedule($schedule)
    {
        $scheduleDetail = DB::select('call sp_schedule_detail_list(?)', [$schedule->Id_Schedule]);
        $invoices = array_map(fn($sd) => $sd->Id_Invoice, $scheduleDetail);
        $bookingTourPassengers = $this->invoiceAnalyzer->findBookingPackages($invoices);
        $schedule->Available = true;

        if ($schedule->Schedule_IgnoreAvailabilityCheck == 2) {
            return $schedule;
        }

        if (config('var.SKIP_SCHEDULE_PACKAGE_AVAILABILITY', false)) {
            return $schedule;
        }

        try {
            foreach ($bookingTourPassengers as $bookingTourPassenger) {
                $this->packageAvailability->generateCalendar($bookingTourPassenger);
            }
        } catch (BookingNotAvailable $e) {
            $schedule->Available = false;
        }
        return $schedule;
    }
}
