<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Cart\LegacyOrderImport;

final readonly class LegacyOrderSourceConnectionConfig
{
    public const DEFAULT_HOST = '127.0.0.1';
    public const DEFAULT_MYSQL_PORT = 3306;
    public const DEFAULT_SSH_PORT = 22;

    public function __construct(
        public string $database,
        public string $user,
        public string $password,
        public string $host = self::DEFAULT_HOST,
        public int $port = self::DEFAULT_MYSQL_PORT,
        public ?string $sshHost = null,
        public ?string $sshUser = null,
        public ?string $sshKey = null,
        public int $sshPort = self::DEFAULT_SSH_PORT,
    ) {
        $this->assertNotBlank($this->database, 'Le nom de la base MySQL source est obligatoire.');
        $this->assertNotBlank($this->user, 'L utilisateur MySQL source est obligatoire.');
        $this->assertNotBlank($this->host, 'L hote MySQL source est obligatoire.');
        $this->assertPort($this->port, 'Le port MySQL source');
        $this->assertPort($this->sshPort, 'Le port SSH');

        if (null === $this->sshHost) {
            if (null !== $this->sshUser || null !== $this->sshKey) {
                throw new \InvalidArgumentException('Renseignez --ssh-host pour activer le tunnel SSH.');
            }

            return;
        }

        $this->assertNotBlank($this->sshHost, 'L hote SSH est obligatoire.');
        if (str_starts_with($this->sshHost, '-')) {
            throw new \InvalidArgumentException('L hote SSH ne peut pas commencer par un tiret.');
        }

        if (null === $this->sshUser) {
            throw new \InvalidArgumentException('L utilisateur SSH est obligatoire lorsque --ssh-host est renseigne.');
        }
        $this->assertNotBlank($this->sshUser, 'L utilisateur SSH est obligatoire lorsque --ssh-host est renseigne.');

        if (null === $this->sshKey) {
            throw new \InvalidArgumentException('La cle privee SSH est obligatoire lorsque --ssh-host est renseigne.');
        }
        $this->assertNotBlank($this->sshKey, 'La cle privee SSH est obligatoire lorsque --ssh-host est renseigne.');
    }

    public function usesSsh(): bool
    {
        return null !== $this->sshHost;
    }

    /**
     * @return array<string, int|string>
     */
    public function dbalParameters(?int $localPort = null): array
    {
        if ($this->usesSsh() && null === $localPort) {
            throw new \LogicException('Le port local du tunnel SSH est obligatoire.');
        }
        if (!$this->usesSsh() && null !== $localPort) {
            throw new \LogicException('Un port local ne doit etre fourni que pour un tunnel SSH.');
        }

        return [
            'charset' => 'utf8mb4',
            'dbname' => $this->database,
            'driver' => 'pdo_mysql',
            'host' => $this->usesSsh() ? self::DEFAULT_HOST : $this->host,
            'password' => $this->password,
            'port' => $localPort ?? $this->port,
            'user' => $this->user,
        ];
    }

    /**
     * @return list<string>
     */
    public function sshCommand(int $localPort, string $sshExecutable = 'ssh'): array
    {
        if (!$this->usesSsh() || null === $this->sshHost || null === $this->sshUser || null === $this->sshKey) {
            throw new \LogicException('La configuration SSH est incomplete.');
        }
        $this->assertPort($localPort, 'Le port local du tunnel SSH');

        $forwardHost = str_contains($this->host, ':') && !str_starts_with($this->host, '[')
            ? '['.$this->host.']'
            : $this->host;

        return [
            $sshExecutable,
            '-N',
            '-T',
            '-p',
            (string) $this->sshPort,
            '-i',
            $this->sshKey,
            '-o',
            'BatchMode=yes',
            '-o',
            'ConnectTimeout=10',
            '-o',
            'ExitOnForwardFailure=yes',
            '-o',
            'ServerAliveInterval=15',
            '-o',
            'ServerAliveCountMax=2',
            '-o',
            'StrictHostKeyChecking=accept-new',
            '-L',
            \sprintf('%s:%d:%s:%d', self::DEFAULT_HOST, $localPort, $forwardHost, $this->port),
            $this->sshUser.'@'.$this->sshHost,
        ];
    }

    public function mysqlEndpoint(): string
    {
        return \sprintf('%s@%s:%d/%s', $this->user, $this->host, $this->port, $this->database);
    }

    public function sshEndpoint(): ?string
    {
        if (!$this->usesSsh()) {
            return null;
        }

        return \sprintf('%s@%s:%d', $this->sshUser, $this->sshHost, $this->sshPort);
    }

    private function assertNotBlank(string $value, string $message): void
    {
        if ('' === mb_trim($value)) {
            throw new \InvalidArgumentException($message);
        }
    }

    private function assertPort(int $port, string $label): void
    {
        if ($port < 1 || $port > 65535) {
            throw new \InvalidArgumentException($label.' doit etre compris entre 1 et 65535.');
        }
    }
}
