<?php

namespace App\Http\Controllers\Owner\Info;

use App\Http\Controllers\Owner\BookingCreator;
use App\Http\Controllers\Owner\Passenger\PassengerUpdater;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use Illuminate\Support\Facades\DB;

class RedirectGuideMediaController
{

    private array $checkedDirectories = [];
    private array $groupProvidersMap = [];
    private array $groupsMap = [];
    private array $toursMap = [];
    private array $createdDirectories = [];
    private array $groupPassengersMap = [];

    public function __construct(
        private QueryBus $queryBus,
        private PassengerUpdater $passengerUpdater,
        private BookingCreator $bookingCreator
    ) {}

    public function __invoke()
    {
        // $this->groupProviderMedia(10000, 20000);
        $this->groupPassengerMedia(10000, 0);
    }

    private function groupProviderMedia(int $limit, int $offset)
    {
        $dir = config('var.PATH_PRIVATE');
        $redirectDir = 'image' . DIRECTORY_SEPARATOR . 'group_provider' . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR;
        $large = 'large' . DIRECTORY_SEPARATOR;
        $thumb = 'thumb' . DIRECTORY_SEPARATOR;

        $pathKey = 'GroupProviderMedia_Path';
        $widthKey = $large;

        $groupProviderMedia = $this->getGroupProviderMedia($limit, $offset);
        foreach ($groupProviderMedia as $i => $media) {
            list($directory, $path) = $this->divideDirectoryAndPath($media->$pathKey);
            if (!$this->checkDirectoryExists($dir . $directory)) {
                continue;
            }
            if (!$this->checkFileExists($dir . $directory . DIRECTORY_SEPARATOR . $path)) {
                continue;
            }

            list($groupProvider, $group, $tour) = $this->getProviderData($media);
            $mysqlPath = $redirectDir . $widthKey . $this->getNameOfGroupProviderMedia($groupProvider, $group, $tour);
            $dirOfMedia = config('var.PATH_PRIVATE') . $mysqlPath;

            $this->createDirectory($dirOfMedia);
            $this->redirectGroupProviderMedia(
                $media->Id_GroupProviderMedia,
                $media->$pathKey,
                $dir . $directory . DIRECTORY_SEPARATOR . $path,
                $dirOfMedia . DIRECTORY_SEPARATOR . $path
            );
            echo ($media->Id_GroupProviderMedia . ': ' . $dirOfMedia . '<br>');
            $media->$pathKey = $mysqlPath . DIRECTORY_SEPARATOR . $path;
            $this->updateProviderMedia($media, $pathKey);
        }
    }

    private function groupPassengerMedia($limit, $offset)
    {
        $dir = config('var.PATH_PRIVATE');
        $redirectDir = 'image' . DIRECTORY_SEPARATOR . 'group_passenger' . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR;
        $large = 'large' . DIRECTORY_SEPARATOR;
        $thumb = 'thumb' . DIRECTORY_SEPARATOR;

        $pathKey = 'GroupPassengerMedia_Path';
        $widthKey = $large;

        $groupPassengerMedia = $this->getGroupPassengerMedia($limit, $offset);
        foreach ($groupPassengerMedia as $i => $media) {
            list($directory, $path) = $this->divideDirectoryAndPath($media->$pathKey);
            if (!$this->checkDirectoryExists($dir . $directory)) {
                continue;
            }
            if (!$this->checkFileExists($dir . $directory . DIRECTORY_SEPARATOR . $path)) {
                continue;
            }

            list($groupPassenger, $group, $tour) = $this->getPassengerData($media);
            $mysqlPath = $redirectDir . $widthKey . $this->getNameOfGroupPassengerMedia($groupPassenger, $group, $tour);
            $dirOfMedia = config('var.PATH_PRIVATE') . $mysqlPath;

            $this->createDirectory($dirOfMedia);
            $this->redirectGroupPassengerMedia(
                $media->Id_GroupPassengerMedia,
                $media->$pathKey,
                $dir . $directory . DIRECTORY_SEPARATOR . $path,
                $dirOfMedia . DIRECTORY_SEPARATOR . $path
            );
            echo ($media->Id_GroupPassengerMedia . ': ' . $dirOfMedia . '<br>');
            $media->$pathKey = $mysqlPath . DIRECTORY_SEPARATOR . $path;
            $this->updatePassengerMedia($media, $pathKey);
        }
    }

    private function updateProviderMedia($media, $pathKey)
    {
        DB::update(
            "UPDATE t_group_provider_media
            SET `$pathKey` = ?
            WHERE Id_GroupProviderMedia = ?",
            [
                $media->$pathKey,
                $media->Id_GroupProviderMedia
            ]
        );
    }

    private function updatePassengerMedia($media, $pathKey)
    {
        DB::update(
            "UPDATE t_group_passenger_media
            SET `$pathKey` = ?
            WHERE Id_GroupPassengerMedia = ?",
            [
                $media->$pathKey,
                $media->Id_GroupPassengerMedia
            ]
        );
    }

    private function redirectGroupProviderMedia($idGroupProviderMedia, $mediaKey, $originPath, $redirectPath)
    {
        if (!rename($originPath, $redirectPath)) {
            // Si falla (por ejemplo, entre discos distintos), intenta copiar y eliminar
            if (!copy($originPath, $redirectPath)) {
                throw new \RuntimeException("No se pudo mover el archivo a: {$redirectPath}");
            }
            unlink($originPath);
        }

        return (object)[
            'Id_GroupProviderMedia' => $idGroupProviderMedia,
            'Media_Key' => $mediaKey,
            'GroupProviderMedia_Path' => $redirectPath,
            'GroupProviderMedia_OriginPath' => $originPath
        ];
    }

    private function createDirectory($dir)
    {
        if (!isset($this->createdDirectories[$dir])) {
            if (is_dir($dir)) {
                $this->createdDirectories[$dir] = $dir;
                return;
            }
            mkdir($dir);
            $this->createdDirectories[$dir] = $dir;
        }
    }

    private function getProviderData($media)
    {
        $groupProvider = $this->getGroupProvider($media->Id_GroupProvider);
        $group = $this->getGroup($groupProvider->Id_Group);
        $tour = $this->getTour($group->Id_Service);
        return [$groupProvider, $group, $tour];
    }

    private function getPassengerData($media)
    {
        $groupPassenger = $this->getGroupPassenger($media->Id_GroupPassenger);
        $group = $this->getGroup($groupPassenger->Id_Group);
        $tour = $this->getTour($group->Id_Service);
        return [$groupPassenger, $group, $tour];
    }

    private function getNameOfGroupProviderMedia($groupProvider, $group, $tour)
    {
        return $group->Group_DateStart . ' ' . $groupProvider->Provider_NickName . ' (' . $tour->Tour_Code . ')';
    }

    private function redirectGroupPassengerMedia($idGroupPassengerMedia, $mediaKey, $originPath, $redirectPath)
    {
        if (!rename($originPath, $redirectPath)) {
            // Si falla (por ejemplo, entre discos distintos), intenta copiar y eliminar
            if (!copy($originPath, $redirectPath)) {
                throw new \RuntimeException("No se pudo mover el archivo a: {$redirectPath}");
            }
            unlink($originPath);
        }

        return (object)[
            'Id_GroupPassengerMedia' => $idGroupPassengerMedia,
            'Media_Key' => $mediaKey,
            'GroupPassengerMedia_Path' => $redirectPath,
            'GroupPassengerMedia_OriginPath' => $originPath
        ];
    }

    private function getNameOfGroupPassengerMedia($groupPassenger, $group, $tour)
    {
        return $group->Group_DateStart . ' ' . $groupPassenger->Booking_Code
            . ' ' . $groupPassenger->Passenger_Name
            . ' ' . $groupPassenger->Passenger_LastName
            . ' (' . $tour->Tour_Code . ')';
    }

    private function getGroupProvider($idGroupProvider)
    {
        if (isset($this->groupProvidersMap[$idGroupProvider])) {
            return $this->groupProvidersMap[$idGroupProvider];
        }
        $this->groupProvidersMap[$idGroupProvider] = DB::select('SELECT *
            FROM `t_group_provider` `gp`
            INNER JOIN `t_provider` `p` ON `gp`.`Id_Provider` = `p`.`Id_Provider`
            WHERE `Id_GroupProvider` = ?', [$idGroupProvider])[0];
        return $this->groupProvidersMap[$idGroupProvider];
    }

    private function getGroupPassenger($idGroupPassenger)
    {
        if (isset($this->groupPassengersMap[$idGroupPassenger])) {
            return $this->groupPassengersMap[$idGroupPassenger];
        }
        $this->groupPassengersMap[$idGroupPassenger] = DB::select('SELECT *
            FROM `t_group_passenger` `gp`
            INNER JOIN `t_booking_tour_passenger` `btp` ON `gp`.`Id` = `btp`.`Id_BookingTourPassenger`
            INNER JOIN `t_passenger` `p` ON `btp`.`Id_Passenger` = `p`.`Id_Passenger`
            INNER JOIN `t_booking` `b` ON `p`.`Id_Booking` = `b`.`Id_Booking`
            WHERE `gp`.`Id_GroupPassenger` = ?
            AND `gp`.`GroupPassenger_Type` = 1', [$idGroupPassenger])[0];
        return $this->groupPassengersMap[$idGroupPassenger];
    }

    /**
     * Find group in groupsMap or query database
     */
    private function getGroup($idGroup)
    {
        if (isset($this->groupsMap[$idGroup])) {
            return $this->groupsMap[$idGroup];
        }
        $this->groupsMap[$idGroup] = DB::select('SELECT *
            FROM `t_group` `g`
            WHERE `Id_Group` = ?
            AND `g`.`Group_ServiceType` = 1
            AND `g`.`Group_Status` = 2', [$idGroup])[0];
        return $this->groupsMap[$idGroup];
    }

    private function getTour($idTour)
    {
        if (isset($this->toursMap[$idTour])) {
            return $this->toursMap[$idTour];
        }
        $this->toursMap[$idTour] = DB::select('SELECT *
            FROM `t_tour` `t`
            WHERE `Id_Tour` = ?', [$idTour])[0];
        return $this->toursMap[$idTour];
    }

    private function checkDirectoryExists(string $directory): bool
    {
        // Si ya fue verificado, no repetir
        if (isset($this->checkedDirectories[$directory])) {
            return true;
        }

        // Verifica existencia real
        if (!is_dir($directory)) {
            return false;
        }

        // Marca como revisado
        $this->checkedDirectories[$directory] = true;

        return true;
    }

    private function checkFileExists(string $file): bool
    {
        return file_exists($file);
    }

    private function getGroupProviderMedia($limit, $offset)
    {
        return DB::select('SELECT * FROM `t_group_provider_media` LIMIT ?,?', [$offset, $limit]);
    }

    /**
     * Divide the given path into directory and path
     * @param string $path
     * @return array
     */
    private function divideDirectoryAndPath(string $path)
    {
        // Normaliza las barras invertidas en sistemas Windows
        $normalizedPath = str_replace('\\', '/', $path);

        // Obtiene el directorio y el archivo usando funciones nativas
        $directory = rtrim(dirname($normalizedPath), '/');
        $filename  = basename($normalizedPath);

        return [$directory, $filename];
    }

    private function getGroupPassengerMedia()
    {
        return DB::select('SELECT * FROM `t_group_passenger_media`');
    }
}
