<?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';
    private const NONCE_ACTION = 'feca_mailshots_setup';

    private WordPressFacade $wp;

    public function __construct(WordPressFacade $wp)
    {
        $this->wp = $wp;
    }

    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
    {
        $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();
        $status = $this->wp->requestParam('saved', '') === '1' ? 'Settings saved.' : '';
        $test = $this->testResult();
        $action = htmlspecialchars($this->wp->adminUrl('admin-post.php'));

        echo '<div class="wrap">';
        echo '<h1>FECA Mailshots Setup</h1>';
        if ($status !== '') {
            echo '<div style="padding:10px;border:1px solid #8bc34a;background:#f1f8e9;margin:12px 0;">' . htmlspecialchars($status) . '</div>';
        }
        if ($test !== null) {
            $ok = !empty($test['ok']);
            $bg = $ok ? '#f1f8e9' : '#ffebee';
            $border = $ok ? '#8bc34a' : '#ef9a9a';
            $title = $ok ? 'Connection test passed.' : 'Connection test failed.';
            echo '<div style="padding:10px;border:1px solid ' . $border . ';background:' . $bg . ';margin:12px 0;">';
            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 . '">';
        echo $this->hiddenNonceField(self::NONCE_ACTION);

        $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'] ?? '');

        echo '<p>';
        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', '') ?? '')),
        ];

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

        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', '') ?? '')),
        ];

        $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'] as $key) {
            $out[$key] = isset($raw[$key]) ? (string) $raw[$key] : '';
        }
        return $out;
    }

    private function field(string $label, string $name, string $value, string $type = 'text'): void
    {
        echo '<table class="form-table" role="presentation"><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) . '"></td>';
        echo '</tr></table>';
    }

    /** @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'] 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'] 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']),
        ];
    }
}
