<?php

namespace App\Modules\Room\Domain;

use App\Http\Controllers\Owner\Room\RoomListOrderBy;
use App\Http\Controllers\Owner\Room\RoomListOrderType;
use App\Modules\DefinedService\Location\Domain\IdLocation;
use App\Modules\Shared\Domain\DateEnd;
use App\Modules\Shared\Domain\DateStart;
use App\Modules\Shared\Domain\QueryBuilder\Operator\OperatorAnd;
use App\Modules\Shared\Domain\QueryBuilder\Operator\OperatorBetween;
use App\Modules\Shared\Domain\QueryBuilder\Operator\OperatorEqual;
use App\Modules\Shared\Domain\QueryBuilder\Operator\OperatorAllIn;
use App\Modules\Shared\Domain\QueryBuilder\Operator\OperatorIn;
use App\Modules\Shared\Domain\QueryBuilder\Operator\OperatorLike;
use App\Modules\Shared\Domain\QueryBuilder\Operator\OperatorOr;
use App\Modules\Shared\Domain\Storage\Criteria\StorageCriteria;
use Illuminate\Support\Facades\DB;

class CountListAllRoom implements StorageCriteria
{
    public function __construct(
        private DateStart $dateStart,
        private DateEnd $dateEnd,
        private RoomListOrderBy $orderBy,
        private RoomListOrderType $orderType,
        private array $statuses,
        private array $idExtras,
        private array $idProviders,
        private array $locks,
        private array $idCategories,
        private array $idTours,
        private array $idTypeRooms,
        private array $UpgradeStatuses,
        private bool $allStatuses,
        private bool $allExtras,
        private bool $allProviders,
        private bool $allLocks,
        private bool $allTours,
        private bool $allTypeRooms,
        private bool $allUpgradeStatuses,
        private IdLocation $idLocation,
        private ?string $search,
        private ?int $idRoom = null,
    ) {}

    public function run()
    {
        $dates = [
            RoomListOrderBy::ROOM => '`r`.`Room_RequestDateStart`',
            RoomListOrderBy::TOUR => '`bt`.`BookingTour_DateStart`',
            RoomListOrderBy::ROOM_RESERVATION_DATE_FINAL => '`r`.`Room_ReservationDateFinal`',
        ];

        if ($this->idLocation->value() == 0) {
            $locationOperator = new OperatorEqual('1', '1');
        } else {
            $locationOperator = new OperatorEqual('`e`.`Id_Location`', $this->idLocation->value());
        }

        $idRoomOperator = $this->idRoom !== null
            ? new OperatorEqual('`r`.`Id_Room`', $this->idRoom)
            : new OperatorEqual('1', '1');

        $searchOperator = $this->search
            ? new OperatorOr(
                new OperatorLike('`p`.`Passenger_Name`', '%' . $this->search . '%'),
                new OperatorLike('`p`.`Passenger_LastName`', '%' . $this->search . '%'),
                new OperatorLike('`p`.`Passenger_NoDocument`', '%' . $this->search . '%'),
                new OperatorLike('`b`.`Booking_Code`', '%' . $this->search . '%'),
                new OperatorLike('`pr`.`Provider_NickName`', '%' . $this->search . '%'),
                new OperatorLike('`t`.`Tour_Code`', '%' . $this->search . '%'),
                new OperatorLike('`pk`.`Package_Code`', '%' . $this->search . '%'),
                new OperatorLike('`gprov_pv`.`Provider_NickName`', '%' . $this->search . '%'),
            )
            : new OperatorEqual('1', '1');

        $operator = new OperatorAnd(
            new OperatorBetween($dates[$this->orderBy->value()], $this->dateStart->value() . ' 00:00:00', $this->dateEnd->value() . ' 23:59:59'),
            new OperatorAllIn('`r`.`Room_Status`', $this->allStatuses, $this->statuses),
            new OperatorAllIn('`e`.`Id_Extra`', $this->allExtras, $this->idExtras),
            new OperatorAllIn('`r`.`Id_Provider`', $this->allProviders, [0, ...$this->idProviders]),
            new OperatorAllIn('`r`.`Room_Lock`', $this->allLocks, $this->locks),
            new OperatorAllIn('`bt`.`Id_Tour`', $this->allTours, $this->idTours),
            new OperatorAllIn('`r`.`Id_TypeRoom`', $this->allTypeRooms, $this->idTypeRooms),
            new OperatorAllIn('`be`.`BookingExtra_Mode`', $this->allUpgradeStatuses, $this->UpgradeStatuses),
            $locationOperator,
            $searchOperator,
            $idRoomOperator,
        );

        $table = Room::table();

        $result = DB::select(
            "SELECT COUNT(DISTINCT `r`.`Id_Room`) as `total`
            FROM `$table` `r`
            INNER JOIN `t_type_room` `tr` ON `tr`.`Id_TypeRoom` = `r`.`Id_TypeRoom`
            INNER JOIN `t_booking_extra` `be` ON `be`.`Id_BookingExtra` = `r`.`Id_BookingExtra`
            INNER JOIN `t_extra` `e` ON `e`.`Id_Extra` = `be`.`Id_Extra`
            INNER JOIN `t_booking_tour` `bt` ON `bt`.`Id_BookingTour` = `be`.`Id_BookingTour`
            INNER JOIN `t_booking` `b` ON `b`.`Id_Booking` = `bt`.`Id_Booking`
            INNER JOIN `t_tour` `t` ON `t`.`Id_Tour` = `bt`.`Id_Tour`
            INNER JOIN `t_package` `pk` ON `pk`.`Id_Package` = `bt`.`Id_Package`
            LEFT JOIN `t_provider` `pr` ON `pr`.`Id_Provider` = `r`.`Id_Provider`
            LEFT JOIN `t_room_passenger` `rp` ON `rp`.`Id_Room` = `r`.`Id_Room` AND `rp`.`RoomPassenger_Status` <> 0
            LEFT JOIN `t_booking_extra_passenger` `bep` ON `bep`.`Id_BookingExtraPassenger` = `rp`.`Id_BookingExtraPassenger`
            LEFT JOIN `t_booking_tour_passenger` `btp` ON `btp`.`Id_BookingTourPassenger` = `bep`.`Id_BookingTourPassenger`
            " . ($this->search ? "
            LEFT JOIN `t_group_passenger` `gprov_gp` ON `gprov_gp`.`Id` = `btp`.`Id_BookingTourPassenger` AND `gprov_gp`.`GroupPassenger_Type` = 1
            LEFT JOIN `t_group` `gprov_g` ON `gprov_g`.`Id_Group` = `gprov_gp`.`Id_Group`
            LEFT JOIN `t_group_provider` `gprov_gpv` ON `gprov_gpv`.`Id_Group` = `gprov_g`.`Id_Group` AND `gprov_gpv`.`Id_TypeProvider` = 1
            LEFT JOIN `t_provider` `gprov_pv` ON `gprov_pv`.`Id_Provider` = `gprov_gpv`.`Id_Provider`
            " : "") . "
            LEFT JOIN `t_passenger` `p` ON `p`.`Id_Passenger` = `btp`.`Id_Passenger`
            WHERE {$operator->query()}
            AND
            CASE
                WHEN `bep`.`BookingExtraPassenger_Status` = 2 THEN 1
                WHEN `bep`.`BookingExtraPassenger_Status` = 1 THEN
                `btp`.`BookingTourPassenger_Status` IN (2, 3)
                AND `fn_booking_tour_passenger_check`(
                    `b`.`Booking_Status`, `b`.`Booking_ManuallyConfirmed`, 1, 1, `btp`.`BookingTourPassenger_PaymentStatus`
                )
            END
            AND `e`.`Id_TypeExtra` = 4
            ",
            $operator->values()
        );

        return $result;
    }
}
