<?php

declare(strict_types=1);

namespace External\HannahBundle\Util\FTP;

use External\HannahBundle\Util\Configuration;
use FTP\Connection;
use KupShop\KupShopBundle\Util\StringUtil;
use Symfony\Component\ErrorHandler\ErrorHandler;

/**
 * Pripojuje se k FTP, kde jsou obrazky a dalsi data, ktere jsou potreba importovat.
 */
class FTPClient
{
    private const FTP = [
        'host' => 'ftp.hannah.cz',
        'user' => 'wpj',
        'pass' => 'mnbv14789',
    ];

    /**
     * @var Connection[]
     */
    private array $connections = [];

    private ?string $ftpBaseDir = null;

    private array $filesCache = [];

    protected array $credentials = [];

    public function __construct(
        private readonly Configuration $configuration,
    ) {
    }

    public function withCredentials(array $credentials, callable $fn, ?string $baseDir = null): mixed
    {
        $this->credentials = [
            'user' => $credentials[0],
            'password' => $credentials[1],
        ];
        $this->ftpBaseDir = $baseDir;

        $result = $fn();

        $this->credentials = [];
        $this->ftpBaseDir = null;

        return $result;
    }

    public function getProductPhotos(string $code): array
    {
        $photos = $this->getPhotosCache()[$code] ?? [];

        $photosPositions = [];
        // nactu si pozice fotek
        foreach ($photos as $photo) {
            $position = 0;
            if ($filename = (pathinfo($photo)['filename'] ?? null)) {
                $parts = explode('_', $filename);
                $position = end($parts);
            }

            // pokud to neni cislo, nebo to je nesmyslna pozice
            if (!is_numeric($position) || $position > 100 || (strlen($position) > 1 && StringUtil::startsWith($position, '0'))) {
                $position = 0;
            }

            $photosPositions[$photo] = (int) $position;
        }

        // seradim fotky podle pozic
        uasort($photosPositions, function ($item1, $item2) {
            return $item1 <=> $item2;
        });

        return array_keys($photosPositions);
    }

    public function getPhotosCache(): array
    {
        if (empty($this->filesCache)) {
            $this->loadFiles($this->filesCache, extensions: ['jpg', 'jpeg', 'png'], groupFn: function (string $filename) {
                $group = explode('_', $filename)[0];
                if ($group === $filename) {
                    $group = 'other';
                }

                return $group;
            });
        }

        return $this->filesCache;
    }

    public function getPricesFiles(string $folder): array
    {
        $files = [];

        $this->loadFiles($files, $folder, ['csv'], recursive: false);

        return $files;
    }

    public function copyFile(string $src, string $dest): bool
    {
        if (file_exists($dest)) {
            return true;
        }

        return ftp_get($this->getFTP(), $dest, $src);
    }

    public function renameFile(string $src, string $filename): bool
    {
        return ftp_rename($this->getFTP(), $src, $filename);
    }

    public function loadFiles(array &$foundFiles = [], ?string $directory = null, ?array $extensions = null, ?callable $groupFn = null, bool $recursive = true): void
    {
        if (!$directory) {
            $directory = $this->getFTPBaseDir();
        }

        $files = ftp_nlist($this->getFTP(), $directory);
        if ($files === false) {
            return;
        }

        foreach ($files as &$tmpFile) {
            try {
                $tmpFile = ErrorHandler::call(fn () => iconv('windows-1250', 'UTF-8', $tmpFile));
            } catch (\Throwable) {
            }
        }

        foreach ($files as $file) {
            $pathInfo = pathinfo($file);
            $ext = $pathInfo['extension'] ?? false;

            if ($extensions && $ext && !in_array($ext, $extensions)) {
                continue;
            }

            if (!($name = ($pathInfo['basename'] ?? null))) {
                return;
            }

            $filepath = $directory.'/'.$name;
            // pokud se jedna o slozku
            if (!$ext) {
                // pokud mam vypnute rekurzivni prochazeni, tak pokracuju dal
                if (!$recursive) {
                    continue;
                }

                $this->loadFiles($foundFiles, $filepath, extensions: $extensions, groupFn: $groupFn, recursive: $recursive);
            } else {
                if ($groupFn) {
                    $foundFiles[$groupFn($name)][] = $filepath;
                } else {
                    $foundFiles[] = $filepath;
                }
            }
        }
    }

    private function getFTPBaseDir(): string
    {
        if ($this->ftpBaseDir) {
            return $this->ftpBaseDir;
        }

        return $this->configuration->get('ftp.home', '/');
    }

    private function getFTP(): Connection
    {
        if (!($this->connections[$this->getFTPUser()] ?? false)) {
            $this->createFTPConnection();
        }

        if (!is_array(ftp_nlist($this->connections[$this->getFTPUser()], '.'))) {
            $this->connections[$this->getFTPUser()] = null;
            $this->createFTPConnection();
        }

        return $this->connections[$this->getFTPUser()];
    }

    private function createFTPConnection(): void
    {
        $user = $this->getFTPUser();
        $pass = $this->credentials['password'] ?? self::FTP['pass'];

        $this->connections[$user] = ftp_connect(self::FTP['host'], 21, 3600);

        ftp_login($this->connections[$user], $user, $pass);
        ftp_pasv($this->connections[$user], true);
        ftp_chdir($this->connections[$user], $this->getFTPBaseDir());
    }

    public function uploadFile(string $source, string $destination): bool
    {
        return ftp_put($this->getFTP(), $destination, $source);
    }

    private function getFTPUser(): string
    {
        return $this->credentials['user'] ?? self::FTP['user'];
    }
}
