<?php

declare(strict_types=1);

namespace FecaMailshots\Repository;

use FecaMailshots\Application\SecretKeyProvider;
use FecaMailshots\Infrastructure\DatabaseRouter;
use PDO;

final class MailCredentialRepository
{
    private DatabaseRouter $router;
    private SecretKeyProvider $keyProvider;

    public function __construct(DatabaseRouter $router, SecretKeyProvider $keyProvider)
    {
        $this->router = $router;
        $this->keyProvider = $keyProvider;
    }

    /** @return array<string, mixed>|null */
    public function findByUserId(int $userId): ?array
    {
        $this->ensureTable();

        $stmt = $this->router->mailshotsPdo()->prepare('SELECT wp_user_id, smtp_host, smtp_port, smtp_user, smtp_password_enc, smtp_from_email, smtp_from_name, smtp_require_tls, imap_host, imap_port, imap_user, imap_password_enc, imap_sent_folder, imap_mailbox_flags, updated_at FROM mailshot_credentials WHERE wp_user_id = :uid');
        $stmt->execute(['uid' => $userId]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($row === false) {
            return null;
        }

        return [
            'wp_user_id' => (int) $row['wp_user_id'],
            'smtp_host' => (string) $row['smtp_host'],
            'smtp_port' => (int) $row['smtp_port'],
            'smtp_user' => (string) $row['smtp_user'],
            'smtp_password' => $this->decrypt((string) $row['smtp_password_enc']),
            'smtp_from_email' => (string) $row['smtp_from_email'],
            'smtp_from_name' => (string) $row['smtp_from_name'],
            'smtp_require_tls' => (int) $row['smtp_require_tls'] === 1,
            'imap_host' => (string) $row['imap_host'],
            'imap_port' => (int) $row['imap_port'],
            'imap_user' => (string) $row['imap_user'],
            'imap_password' => $this->decrypt((string) $row['imap_password_enc']),
            'imap_sent_folder' => (string) $row['imap_sent_folder'],
            'imap_mailbox_flags' => (string) $row['imap_mailbox_flags'],
            'updated_at' => (string) $row['updated_at'],
        ];
    }

    /** @param array<string, mixed> $payload */
    public function upsertForUser(int $userId, array $payload): void
    {
        $this->ensureTable();

        foreach ([
            'smtp_host',
            'smtp_port',
            'smtp_user',
            'smtp_password',
            'smtp_from_email',
            'smtp_from_name',
            'imap_host',
            'imap_port',
            'imap_user',
            'imap_password',
            'imap_sent_folder',
            'imap_mailbox_flags',
        ] as $required) {
            if (!array_key_exists($required, $payload)) {
                throw new \RuntimeException('Missing required credential field: ' . $required);
            }
        }
        $smtpPassword = trim((string) $payload['smtp_password']);
        $imapPassword = trim((string) $payload['imap_password']);
        if ($smtpPassword === '' || $imapPassword === '') {
            throw new \RuntimeException('SMTP and IMAP passwords are required.');
        }

        $sql = 'INSERT INTO mailshot_credentials (wp_user_id, smtp_host, smtp_port, smtp_user, smtp_password_enc, smtp_from_email, smtp_from_name, smtp_require_tls, imap_host, imap_port, imap_user, imap_password_enc, imap_sent_folder, imap_mailbox_flags)
                VALUES (:wp_user_id, :smtp_host, :smtp_port, :smtp_user, :smtp_password_enc, :smtp_from_email, :smtp_from_name, :smtp_require_tls, :imap_host, :imap_port, :imap_user, :imap_password_enc, :imap_sent_folder, :imap_mailbox_flags)
                ON DUPLICATE KEY UPDATE
                  smtp_host = VALUES(smtp_host),
                  smtp_port = VALUES(smtp_port),
                  smtp_user = VALUES(smtp_user),
                  smtp_password_enc = VALUES(smtp_password_enc),
                  smtp_from_email = VALUES(smtp_from_email),
                  smtp_from_name = VALUES(smtp_from_name),
                  smtp_require_tls = VALUES(smtp_require_tls),
                  imap_host = VALUES(imap_host),
                  imap_port = VALUES(imap_port),
                  imap_user = VALUES(imap_user),
                  imap_password_enc = VALUES(imap_password_enc),
                  imap_sent_folder = VALUES(imap_sent_folder),
                  imap_mailbox_flags = VALUES(imap_mailbox_flags),
                  updated_at = CURRENT_TIMESTAMP';

        $stmt = $this->router->mailshotsPdo()->prepare($sql);
        $stmt->execute([
            'wp_user_id' => $userId,
            'smtp_host' => trim((string) $payload['smtp_host']),
            'smtp_port' => (int) $payload['smtp_port'],
            'smtp_user' => trim((string) $payload['smtp_user']),
            'smtp_password_enc' => $this->encrypt($smtpPassword),
            'smtp_from_email' => trim((string) $payload['smtp_from_email']),
            'smtp_from_name' => trim((string) $payload['smtp_from_name']),
            'smtp_require_tls' => !empty($payload['smtp_require_tls']) ? 1 : 0,
            'imap_host' => trim((string) $payload['imap_host']),
            'imap_port' => (int) $payload['imap_port'],
            'imap_user' => trim((string) $payload['imap_user']),
            'imap_password_enc' => $this->encrypt($imapPassword),
            'imap_sent_folder' => trim((string) $payload['imap_sent_folder']),
            'imap_mailbox_flags' => trim((string) $payload['imap_mailbox_flags']),
        ]);
    }

    private function ensureTable(): void
    {
        $sql = "CREATE TABLE IF NOT EXISTS mailshot_credentials (
            wp_user_id BIGINT UNSIGNED NOT NULL,
            smtp_host VARCHAR(255) NOT NULL,
            smtp_port INT NOT NULL,
            smtp_user VARCHAR(255) NOT NULL,
            smtp_password_enc LONGTEXT NOT NULL,
            smtp_from_email VARCHAR(255) NOT NULL,
            smtp_from_name VARCHAR(255) DEFAULT NULL,
            smtp_require_tls TINYINT(1) NOT NULL DEFAULT 0,
            imap_host VARCHAR(255) DEFAULT NULL,
            imap_port INT DEFAULT 993,
            imap_user VARCHAR(255) DEFAULT NULL,
            imap_password_enc LONGTEXT DEFAULT NULL,
            imap_sent_folder VARCHAR(255) DEFAULT 'Sent',
            imap_mailbox_flags VARCHAR(64) DEFAULT '/imap/ssl',
            updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (wp_user_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci";
        $this->router->mailshotsPdo()->exec($sql);
    }

    private function encrypt(string $plaintext): string
    {
        $key = hash('sha256', $this->keyProvider->keyMaterial(), true);
        $iv = random_bytes(16);
        $ciphertext = openssl_encrypt($plaintext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
        if (!is_string($ciphertext)) {
            throw new \RuntimeException('Credential encryption failed.');
        }
        return base64_encode($iv . $ciphertext);
    }

    private function decrypt(string $encoded): string
    {
        if (trim($encoded) === '') {
            return '';
        }
        $raw = base64_decode($encoded, true);
        if (!is_string($raw) || strlen($raw) < 17) {
            throw new \RuntimeException('Credential decryption failed (invalid payload).');
        }
        $iv = substr($raw, 0, 16);
        $ciphertext = substr($raw, 16);
        $key = hash('sha256', $this->keyProvider->keyMaterial(), true);
        $plaintext = openssl_decrypt($ciphertext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
        if (!is_string($plaintext)) {
            throw new \RuntimeException('Credential decryption failed.');
        }
        return $plaintext;
    }
}
