<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\ParametreMail;

final class SmtpConfigurationService extends BaseService
{
    private const ALLOWED_SECURITY = ['none', 'tls', 'ssl'];

    public function __construct(
        private readonly ParametreMail $settings = new ParametreMail()
    ) {
    }

    /** @return array<string,mixed> */
    public function getForForm(): array
    {
        $settings = $this->settings->get();
        $settings['smtp_password'] = '';
        $settings['password_configured'] = trim(
            (string)($settings['smtp_password_encrypted'] ?? '')
        ) !== '';
        unset($settings['smtp_password_encrypted']);

        return $settings;
    }

    /** @return array<string,mixed> */
    public function getRuntimeConfiguration(): array
    {
        $settings = $this->settings->get();
        $encryptedPassword = trim(
            (string)($settings['smtp_password_encrypted'] ?? '')
        );

        $settings['smtp_password'] = $encryptedPassword !== ''
            ? $this->decrypt($encryptedPassword)
            : '';
        unset($settings['smtp_password_encrypted']);

        return $settings;
    }

    /** @param array<string,mixed> $data */
    public function save(array $data): void
    {
        $current = $this->settings->get();
        $validated = $this->validate($data);
        $plainPassword = (string)($data['smtp_password'] ?? '');

        if ($plainPassword !== '') {
            $validated['smtp_password_encrypted'] = $this->encrypt($plainPassword);
        } else {
            $validated['smtp_password_encrypted'] =
                $current['smtp_password_encrypted'] ?? null;
        }

        if (
            !empty($validated['actif'])
            && trim((string)$validated['smtp_password_encrypted']) === ''
        ) {
            throw new \InvalidArgumentException(
                'Le mot de passe SMTP est obligatoire pour activer la messagerie.'
            );
        }

        $this->settings->save($validated);
    }

    /** @return array<string,mixed> */
    private function validate(array $data): array
    {
        $host = trim((string)($data['smtp_host'] ?? ''));
        $port = (int)($data['smtp_port'] ?? 0);
        $secure = strtolower(trim((string)($data['smtp_secure'] ?? 'tls')));
        $username = trim((string)($data['smtp_username'] ?? ''));
        $fromName = trim((string)($data['smtp_from_name'] ?? ''));
        $fromEmail = trim((string)($data['smtp_from_email'] ?? ''));
        $replyTo = trim((string)($data['smtp_reply_to'] ?? ''));
        $testEmail = trim((string)($data['smtp_test_email'] ?? ''));

        if ($host === '') {
            throw new \InvalidArgumentException('Le serveur SMTP est obligatoire.');
        }
        if ($port < 1 || $port > 65535) {
            throw new \InvalidArgumentException('Le port SMTP doit être compris entre 1 et 65535.');
        }
        if (!in_array($secure, self::ALLOWED_SECURITY, true)) {
            throw new \InvalidArgumentException('Le mode de chiffrement SMTP est invalide.');
        }
        if ($username === '') {
            throw new \InvalidArgumentException('L’identifiant SMTP est obligatoire.');
        }
        if ($fromName === '') {
            throw new \InvalidArgumentException('Le nom de l’expéditeur est obligatoire.');
        }
        $this->assertEmail($fromEmail, 'L’adresse e-mail de l’expéditeur');
        if ($replyTo !== '') {
            $this->assertEmail($replyTo, 'L’adresse de réponse');
        }
        if ($testEmail !== '') {
            $this->assertEmail($testEmail, 'L’adresse e-mail de test');
        }

        return [
            'smtp_host' => $host,
            'smtp_port' => $port,
            'smtp_secure' => $secure,
            'smtp_username' => $username,
            'smtp_from_name' => $fromName,
            'smtp_from_email' => $fromEmail,
            'smtp_reply_to' => $replyTo,
            'smtp_test_email' => $testEmail,
            'actif' => !empty($data['actif']) ? 1 : 0,
        ];
    }

    private function assertEmail(string $email, string $label): void
    {
        if ($email === '' || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            throw new \InvalidArgumentException($label . ' n’est pas valide.');
        }
    }

    private function encrypt(string $plainText): string
    {
        $key = $this->encryptionKey();
        $iv = random_bytes(12);
        $tag = '';
        $cipherText = openssl_encrypt(
            $plainText,
            'aes-256-gcm',
            $key,
            OPENSSL_RAW_DATA,
            $iv,
            $tag
        );

        if ($cipherText === false) {
            throw new \RuntimeException('Impossible de chiffrer le mot de passe SMTP.');
        }

        $payload = json_encode([
            'iv' => base64_encode($iv),
            'tag' => base64_encode($tag),
            'data' => base64_encode($cipherText),
        ], JSON_UNESCAPED_SLASHES);

        if ($payload === false) {
            throw new \RuntimeException('Impossible de préparer le mot de passe SMTP chiffré.');
        }

        return base64_encode($payload);
    }

    private function decrypt(string $payload): string
    {
        $decodedPayload = base64_decode($payload, true);
        $values = is_string($decodedPayload)
            ? json_decode($decodedPayload, true)
            : null;

        if (!is_array($values)) {
            throw new \RuntimeException('Le mot de passe SMTP enregistré est illisible.');
        }

        $iv = base64_decode((string)($values['iv'] ?? ''), true);
        $tag = base64_decode((string)($values['tag'] ?? ''), true);
        $data = base64_decode((string)($values['data'] ?? ''), true);

        if (!is_string($iv) || !is_string($tag) || !is_string($data)) {
            throw new \RuntimeException('Le mot de passe SMTP enregistré est invalide.');
        }

        $plainText = openssl_decrypt(
            $data,
            'aes-256-gcm',
            $this->encryptionKey(),
            OPENSSL_RAW_DATA,
            $iv,
            $tag
        );

        if ($plainText === false) {
            throw new \RuntimeException(
                'Impossible de déchiffrer le mot de passe SMTP. Vérifiez APP_KEY.'
            );
        }

        return $plainText;
    }

    private function encryptionKey(): string
    {
        $rawKey = trim((string)(
            $_ENV['MAIL_ENCRYPTION_KEY']
            ?? $_ENV['APP_KEY']
            ?? ''
        ));

        if ($rawKey === '') {
            throw new \RuntimeException(
                'Ajoutez APP_KEY ou MAIL_ENCRYPTION_KEY dans le fichier .env avant d’enregistrer le mot de passe SMTP.'
            );
        }

        return hash('sha256', $rawKey, true);
    }
}
