<?php

declare(strict_types=1);

namespace FecaMailshots\Admin;

use FecaMailshots\WordPress\WordPressFacade;

final class SetupAdminPage
{
    use AdminRequestHelpers;

    public const OPTION_KEY = 'feca_mailshots_db_settings';
    public const TEST_RESULT_OPTION_KEY = 'feca_mailshots_db_test_result';
    public const DOWNLOAD_MEMORY_LIMIT_OPTION = 'feca_mailshots_download_memory_limit';
    private const NONCE_ACTION = 'feca_mailshots_setup';

    private WordPressFacade $wp;
    /** @var list<string> */
    private array $startupErrors;
    private bool $registerRootMenu;

    /** @param list<string> $startupErrors */
    public function __construct(WordPressFacade $wp, array $startupErrors = [], bool $registerRootMenu = false)
    {
        $this->wp = $wp;
        $this->startupErrors = array_values(array_map(static fn($line): string => (string) $line, $startupErrors));
        $this->registerRootMenu = $registerRootMenu;
    }

    public function register(): void
    {
        $this->wp->addAction('admin_menu', [$this, 'registerMenu']);
        $this->wp->addAction('admin_post_feca_mailshots_setup_save', [$this, 'handleSave']);
        $this->wp->addAction('admin_post_feca_mailshots_setup_test', [$this, 'handleTest']);
    }

    public function registerMenu(): void
    {
        if ($this->registerRootMenu) {
            $this->wp->addMenuPage('FECA Mailshots', 'FECA Mailshots', 'manage_options', 'feca-mailshot', [$this, 'render']);
        }
        $this->wp->addSubmenuPage('feca-mailshot', 'Setup', 'Setup', 'manage_options', 'feca-mailshots-setup', [$this, 'render']);
    }

    public function render(): void
    {
        if (!$this->wp->currentUserCan('manage_options')) {
            echo 'Permission denied';
            return;
        }

        $saved = $this->settings();
        $downloadMemoryLimit = trim((string) ($this->wp->getOption(self::DOWNLOAD_MEMORY_LIMIT_OPTION, '')));
        $status = $this->wp->requestParam('saved', '') === '1' ? 'Settings saved.' : '';
        $test = $this->testResult();
        $action = htmlspecialchars($this->wp->adminUrl('admin-post.php'));

        echo '<div class="wrap feca-mailshots-admin">';
        echo $this->renderAdminUiStyles();
        echo '<h1>FECA Mailshots Setup</h1>';
        if ($status !== '') {
            echo '<div class="feca-banner feca-banner-success">' . htmlspecialchars($status) . '</div>';
        }
        if ($this->startupErrors !== []) {
            echo '<div class="feca-banner feca-banner-error">';
            echo '<strong>' . htmlspecialchars('Setup is incomplete.') . '</strong>';
            echo '<ul>';
            foreach ($this->startupErrors as $line) {
                echo '<li>' . htmlspecialchars($line) . '</li>';
            }
            echo '</ul>';
            echo '</div>';
        }
        if ($test !== null) {
            $ok = !empty($test['ok']);
            $title = $ok ? 'Connection test passed.' : 'Connection test failed.';
            $bannerClass = $ok ? 'feca-banner-success' : 'feca-banner-error';
            echo '<div class="feca-banner ' . $bannerClass . '">';
            echo '<strong>' . htmlspecialchars($title) . '</strong>';
            if (!empty($test['messages']) && is_array($test['messages'])) {
                echo '<ul>';
                foreach ($test['messages'] as $line) {
                    echo '<li>' . htmlspecialchars((string) $line) . '</li>';
                }
                echo '</ul>';
            }
            echo '</div>';
        }
        echo '<p>Configure database credentials used by FECA Mailshots in this WordPress environment.</p>';
        echo '<form method="post" action="' . $action . '" class="feca-form feca-form-max-860">';
        echo $this->hiddenNonceField(self::NONCE_ACTION);
        echo '<table class="form-table" role="presentation">';
        $this->field('Host', 'db_host', $saved['db_host'] ?? '');
        $this->field('Port', 'db_port', $saved['db_port'] ?? '3306');
        $this->field('User', 'db_user', $saved['db_user'] ?? '');
        $this->field('Password', 'db_password', $saved['db_password'] ?? '', 'password');
        $this->field('Mailshots DB Name', 'mailshots_db_name', $saved['mailshots_db_name'] ?? '');
        $this->field('Members DB Name', 'members_db_name', $saved['members_db_name'] ?? '');
        $this->field('FEN DB Name', 'fen_db_name', $saved['fen_db_name'] ?? '');
        $this->field(
            'Download PDF Memory Limit',
            'download_memory_limit',
            $downloadMemoryLimit,
            'text',
            'PHP memory_limit value used for Download PDF actions (for example: 256M, 512M, 1G). Suggested value 512M.'
        );
        echo '</table>';

        echo '<p class="feca-button-row">';
        echo '<button type="submit" class="button button-primary" name="action" value="feca_mailshots_setup_save">Save Setup</button> ';
        echo '<button type="submit" class="button" name="action" value="feca_mailshots_setup_test">Test Connection</button>';
        echo '</p>';
        echo '</form>';
        echo '</div>';
    }

    public function handleSave(): void
    {
        if (!$this->enforceMutationGuardOrJson('manage_options', self::NONCE_ACTION)) {
            return;
        }

        $settings = [
            'db_host' => trim((string) ($this->wp->requestParam('db_host', '') ?? '')),
            'db_port' => trim((string) ($this->wp->requestParam('db_port', '3306') ?? '3306')),
            'db_user' => trim((string) ($this->wp->requestParam('db_user', '') ?? '')),
            'db_password' => (string) ($this->wp->requestParam('db_password', '') ?? ''),
            'mailshots_db_name' => trim((string) ($this->wp->requestParam('mailshots_db_name', '') ?? '')),
            'members_db_name' => trim((string) ($this->wp->requestParam('members_db_name', '') ?? '')),
            'fen_db_name' => trim((string) ($this->wp->requestParam('fen_db_name', '') ?? '')),
        ];
        $downloadMemoryLimit = trim((string) ($this->wp->requestParam('download_memory_limit', '') ?? ''));
        if ($downloadMemoryLimit !== '' && $this->memoryLimitToBytes($downloadMemoryLimit) <= 0) {
            $this->wp->sendJson([
                'ok' => false,
                'errors' => ['Invalid download_memory_limit. Use a PHP memory value such as 256M, 512M, or 1G.'],
            ], 400);
            return;
        }

        $this->wp->updateOption(self::OPTION_KEY, $settings);
        $this->wp->updateOption(self::DOWNLOAD_MEMORY_LIMIT_OPTION, $downloadMemoryLimit);

        if (!headers_sent()) {
            $location = $this->wp->adminUrl('admin.php?page=feca-mailshots-setup&saved=1');
            header('Location: ' . $location, true, 302);
            exit;
        }

        $this->wp->sendJson(['ok' => true, 'saved' => true]);
    }

    public function handleTest(): void
    {
        if (!$this->enforceMutationGuardOrJson('manage_options', self::NONCE_ACTION)) {
            return;
        }

        $settings = [
            'db_host' => trim((string) ($this->wp->requestParam('db_host', '') ?? '')),
            'db_port' => trim((string) ($this->wp->requestParam('db_port', '3306') ?? '3306')),
            'db_user' => trim((string) ($this->wp->requestParam('db_user', '') ?? '')),
            'db_password' => (string) ($this->wp->requestParam('db_password', '') ?? ''),
            'mailshots_db_name' => trim((string) ($this->wp->requestParam('mailshots_db_name', '') ?? '')),
            'members_db_name' => trim((string) ($this->wp->requestParam('members_db_name', '') ?? '')),
            'fen_db_name' => trim((string) ($this->wp->requestParam('fen_db_name', '') ?? '')),
        ];

        $result = $this->runConnectionTest($settings);
        $this->wp->updateOption(self::TEST_RESULT_OPTION_KEY, $result);

        if (!headers_sent()) {
            $location = $this->wp->adminUrl('admin.php?page=feca-mailshots-setup&tested=1');
            header('Location: ' . $location, true, 302);
            exit;
        }

        $this->wp->sendJson($result);
    }

    /** @return array<string, string> */
    private function settings(): array
    {
        $raw = $this->wp->getOption(self::OPTION_KEY, []);
        if (!is_array($raw)) {
            return [];
        }
        $out = [];
        foreach (['db_host', 'db_port', 'db_user', 'db_password', 'mailshots_db_name', 'members_db_name', 'fen_db_name'] as $key) {
            $out[$key] = isset($raw[$key]) ? (string) $raw[$key] : '';
        }
        return $out;
    }

    private function field(string $label, string $name, string $value, string $type = 'text', string $hint = ''): void
    {
        echo '<tr>';
        echo '<th scope="row"><label for="' . htmlspecialchars($name) . '">' . htmlspecialchars($label) . '</label></th>';
        echo '<td><input class="regular-text" type="' . htmlspecialchars($type) . '" id="' . htmlspecialchars($name) . '" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value, ENT_QUOTES) . '">';
        if ($hint !== '') {
            echo '<p class="description">' . htmlspecialchars($hint) . '</p>';
        }
        echo '</td>';
        echo '</tr>';
    }

    private function memoryLimitToBytes(string $limit): int
    {
        $v = trim($limit);
        if ($v === '') {
            return 0;
        }
        if ($v === '-1') {
            return -1;
        }

        $unit = strtolower(substr($v, -1));
        if (ctype_alpha($unit)) {
            $num = (float) substr($v, 0, -1);
            switch ($unit) {
                case 'g':
                    return (int) ($num * 1024 * 1024 * 1024);
                case 'm':
                    return (int) ($num * 1024 * 1024);
                case 'k':
                    return (int) ($num * 1024);
                default:
                    return (int) $num;
            }
        }

        return (int) $v;
    }

    /** @return array{ok:bool,messages:list<string>} */
    private function runConnectionTest(array $settings): array
    {
        $messages = [];
        foreach (['db_host', 'db_port', 'db_user', 'mailshots_db_name', 'members_db_name', 'fen_db_name'] as $required) {
            if (($settings[$required] ?? '') === '') {
                $messages[] = sprintf('Missing required field: %s', $required);
            }
        }
        if ($messages !== []) {
            return ['ok' => false, 'messages' => $messages];
        }

        if (!class_exists(\PDO::class)) {
            return ['ok' => false, 'messages' => ['PDO extension is not available on this server.']];
        }

        try {
            $base = sprintf(
                'mysql:host=%s;port=%s;charset=utf8mb4',
                $settings['db_host'],
                $settings['db_port']
            );
            $pdo = new \PDO(
                $base,
                (string) $settings['db_user'],
                (string) ($settings['db_password'] ?? ''),
                [
                    \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
                    \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
                ]
            );
            $pdo->query('SELECT 1');
            $messages[] = 'Connected to MySQL server successfully.';
        } catch (\Throwable $e) {
            return ['ok' => false, 'messages' => ['MySQL server connection failed: ' . $e->getMessage()]];
        }

        foreach (['mailshots_db_name' => 'Mailshots DB', 'members_db_name' => 'Members DB', 'fen_db_name' => 'FEN DB'] as $key => $label) {
            try {
                $dsn = sprintf(
                    'mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4',
                    $settings['db_host'],
                    $settings['db_port'],
                    $settings[$key]
                );
                $pdoDb = new \PDO(
                    $dsn,
                    (string) $settings['db_user'],
                    (string) ($settings['db_password'] ?? ''),
                    [
                        \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
                        \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
                    ]
                );
                $pdoDb->query('SELECT 1');
                $messages[] = $label . ' is reachable: ' . $settings[$key];
            } catch (\Throwable $e) {
                $messages[] = $label . ' failed: ' . $e->getMessage();
                return ['ok' => false, 'messages' => $messages];
            }
        }

        return ['ok' => true, 'messages' => $messages];
    }

    /** @return array{ok:bool,messages:list<string>}|null */
    private function testResult(): ?array
    {
        $raw = $this->wp->getOption(self::TEST_RESULT_OPTION_KEY, null);
        $this->wp->deleteOption(self::TEST_RESULT_OPTION_KEY);
        if (!is_array($raw) || !isset($raw['ok']) || !isset($raw['messages']) || !is_array($raw['messages'])) {
            return null;
        }
        return [
            'ok' => (bool) $raw['ok'],
            'messages' => array_map(static fn($m): string => (string) $m, $raw['messages']),
        ];
    }
}
