<?php

namespace WPSN\Modules\Notifications\Admin;

use WPSN\Core\Capabilities;
use WPSN\Core\Container;
use WPSN\Core\Security;
use WPSN\Core\Queue\QueueRunner;
use WPSN\Admin\TasksWorkbenchContext;
use WPSN\Admin\SettingsRepository;
use WPSN\Modules\Notifications\NotificationsJobs;
use WPSN\Modules\Notifications\NotificationsRepository;

defined('ABSPATH') || exit;

final class NotificationsAdmin
{
    private Container $container;
    private QueueRunner $runner;
    private NotificationsRepository $repo;

    private string $hookSuffix = '';

    public function __construct(Container $container, QueueRunner $runner)
    {
        $this->container = $container;
        $this->runner = $runner;
        $this->repo = new NotificationsRepository();
    }

    public function register(): void
    {
        add_action('admin_menu', [$this, 'addMenu']);

        add_action('admin_post_wpsn_notifications_save', [$this, 'handleSave']);
        add_action('admin_post_wpsn_notifications_action', [$this, 'handleAction']);
        add_action('admin_post_wpsn_notifications_test', [$this, 'handleTest']);

        add_action('admin_enqueue_scripts', [$this, 'enqueueAssets']);
    }

    public function addMenu(): void
    {
        $this->hookSuffix = (string) add_management_page(
            __('SEO Notifications', 'wp-seo-next'),
            __('SEO Notifications', 'wp-seo-next'),
            Capabilities::CAP_TOOLS,
            'wpsn-notifications',
            [$this, 'render']
        );
    }

    public function enqueueAssets(string $hook): void
    {
        if ($this->hookSuffix !== '' && $hook !== $this->hookSuffix) {
            return;
        }
        // No custom assets for now.
    }

    private function pageUrl(array $args = []): string
    {
        return TasksWorkbenchContext::pageUrl('wpsn-notifications', $args);
    }

    public function render(): void
    {
        if (!Capabilities::canManageTools()) {
            wp_die(esc_html__('Permission denied.', 'wp-seo-next'));
        }

        $settings = $this->container->get('settings');
        $tab = isset($_GET['tab']) ? sanitize_key((string) wp_unslash($_GET['tab'])) : 'inbox';
        if (!in_array($tab, ['inbox', 'settings'], true)) {
            $tab = 'inbox';
        }

        $localOnly = (bool) $settings->get('privacy.local_only', false);
        $queueMode = (string) $settings->get('performance.queue.mode', 'auto');

        echo '<div class="wrap">';
        echo '<h1>' . esc_html__('SEO Notifications', 'wp-seo-next') . '</h1>';
        echo '<p class="description">' . esc_html__('Central notifications for critical events (head output changes, queue failures, audit issues). Supports ack + mute.', 'wp-seo-next') . '</p>';

        if (!empty($_GET['wpsn_notice'])) {
            $n = sanitize_key((string) wp_unslash($_GET['wpsn_notice']));
            if ($n === 'saved') {
                echo '<div class="notice notice-success"><p>' . esc_html__('Settings saved.', 'wp-seo-next') . '</p></div>';
            } elseif ($n === 'done') {
                echo '<div class="notice notice-success"><p>' . esc_html__('Action completed.', 'wp-seo-next') . '</p></div>';
            } elseif ($n === 'test_queued') {
                echo '<div class="notice notice-success"><p>' . esc_html__('Test notification queued.', 'wp-seo-next') . '</p></div>';
            }
        }

        if ($localOnly) {
            echo '<div class="notice notice-warning"><p><strong>' . esc_html__('Local-only mode is enabled:', 'wp-seo-next') . '</strong> ' . esc_html__('Slack/webhooks will be blocked; email can still be sent.', 'wp-seo-next') . '</p></div>';
        }

        if ($queueMode === 'manual') {
            echo '<div class="notice notice-info"><p><strong>' . esc_html__('Queue is in Manual mode:', 'wp-seo-next') . '</strong> ' . esc_html__('Notifications are queued but will not be sent automatically. Run the queue from Diagnostics or switch queue mode to Auto.', 'wp-seo-next') . '</p></div>';
        }

        echo '<h2 class="nav-tab-wrapper">';
        echo '<a href="' . esc_url($this->pageUrl(['tab' => 'inbox'])) . '" class="nav-tab ' . ($tab === 'inbox' ? 'nav-tab-active' : '') . '">' . esc_html__('Inbox', 'wp-seo-next') . '</a>';
        echo '<a href="' . esc_url($this->pageUrl(['tab' => 'settings'])) . '" class="nav-tab ' . ($tab === 'settings' ? 'nav-tab-active' : '') . '">' . esc_html__('Settings', 'wp-seo-next') . '</a>';
        echo '</h2>';

        if ($tab === 'settings') {
            $this->renderSettings();
        } else {
            $this->renderInbox();
        }

        echo '</div>';
    }

    private function renderInbox(): void
    {
        $view = isset($_GET['view']) ? sanitize_key((string) wp_unslash($_GET['view'])) : 'open';
        if (!in_array($view, ['open', 'acked', 'muted', 'all'], true)) {
            $view = 'open';
        }

        $source = isset($_GET['source']) ? sanitize_key((string) wp_unslash($_GET['source'])) : '';
        $severity = isset($_GET['severity']) ? sanitize_key((string) wp_unslash($_GET['severity'])) : '';

        echo '<div style="margin:10px 0;">';
        echo '<a class="button ' . ($view === 'open' ? 'button-primary' : '') . '" href="' . esc_url($this->pageUrl(['tab' => 'inbox', 'view' => 'open'])) . '">' . esc_html__('Open', 'wp-seo-next') . '</a> ';
        echo '<a class="button ' . ($view === 'acked' ? 'button-primary' : '') . '" href="' . esc_url($this->pageUrl(['tab' => 'inbox', 'view' => 'acked'])) . '">' . esc_html__('Acknowledged', 'wp-seo-next') . '</a> ';
        echo '<a class="button ' . ($view === 'muted' ? 'button-primary' : '') . '" href="' . esc_url($this->pageUrl(['tab' => 'inbox', 'view' => 'muted'])) . '">' . esc_html__('Muted', 'wp-seo-next') . '</a> ';
        echo '<a class="button ' . ($view === 'all' ? 'button-primary' : '') . '" href="' . esc_url($this->pageUrl(['tab' => 'inbox', 'view' => 'all'])) . '">' . esc_html__('All', 'wp-seo-next') . '</a>';
        echo '</div>';

        $rows = $this->repo->list([
            'view' => $view,
            'source' => $source,
            'severity' => $severity,
        ], 80, 0);

        if (empty($rows)) {
            echo '<p>' . esc_html__('No notifications in this view.', 'wp-seo-next') . '</p>';
            return;
        }

        echo '<table class="widefat striped">';
        echo '<thead><tr>';
        echo '<th style="width:140px;">' . esc_html__('Created', 'wp-seo-next') . '</th>';
        echo '<th style="width:80px;">' . esc_html__('Severity', 'wp-seo-next') . '</th>';
        echo '<th style="width:110px;">' . esc_html__('Source', 'wp-seo-next') . '</th>';
        echo '<th>' . esc_html__('Title', 'wp-seo-next') . '</th>';
        echo '<th style="width:220px;">' . esc_html__('Actions', 'wp-seo-next') . '</th>';
        echo '</tr></thead>';
        echo '<tbody>';

        foreach ($rows as $r) {
            $id = (int) ($r['id'] ?? 0);
            $created = isset($r['created_at']) ? (string) $r['created_at'] : '';
            $sev = isset($r['severity']) ? sanitize_key((string) $r['severity']) : 'info';
            $src = isset($r['source']) ? sanitize_key((string) $r['source']) : '';
            $title = isset($r['title']) ? (string) $r['title'] : '';
            $msg = isset($r['message']) ? (string) $r['message'] : '';
            $url = isset($r['url']) ? (string) $r['url'] : '';
            $meta = [];
            if (!empty($r['meta']) && is_string($r['meta'])) {
                $decoded = json_decode((string) $r['meta'], true);
                if (is_array($decoded)) {
                    $meta = $decoded;
                }
            }

            $acked = !empty($r['acknowledged_at']);
            $muted = !empty($r['muted_until']);

            echo '<tr>';            
            echo '<td>' . esc_html($created) . '</td>';
            echo '<td><strong>' . esc_html(strtoupper($sev)) . '</strong></td>';
            echo '<td>' . esc_html($src) . '</td>';
            echo '<td>';
            echo '<div><strong>' . esc_html($title) . '</strong></div>';
            echo '<div class="description">' . esc_html($msg) . '</div>';
            if ($src === 'platform_ops') {
                $preview = $this->platformOpsNotificationPreview($meta);
                if (!empty($preview['text'])) {
                    echo '<div class="description" style="margin-top:4px;"><strong>' . esc_html__('Payload preview:', 'wp-seo-next') . '</strong> ' . esc_html((string) $preview['text']) . '</div>';
                }
                if (!empty($preview['compare_url'])) {
                    echo '<div style="margin-top:4px;"><a href="' . esc_url((string) $preview['compare_url']) . '">' . esc_html__('Compare platform runs', 'wp-seo-next') . '</a></div>';
                }
            }
            if ($url !== '') {
                echo '<div style="margin-top:4px;"><a href="' . esc_url($url) . '" target="_blank" rel="noopener">' . esc_html__('Open link', 'wp-seo-next') . '</a></div>';
            }
            echo '</td>';

            echo '<td>';

            $base = admin_url('admin-post.php');
            $nonce = wp_create_nonce('wpsn_notifications_action');

            if (!$acked) {
                $ackUrl = add_query_arg([
                    'action' => 'wpsn_notifications_action',
                    'do' => 'ack',
                    'id' => $id,
                    '_wpnonce' => $nonce,
                ], $base);
                echo '<a class="button" href="' . esc_url($ackUrl) . '">' . esc_html__('Ack', 'wp-seo-next') . '</a> ';
            }

            if (!$muted) {
                $m7 = add_query_arg([
                    'action' => 'wpsn_notifications_action',
                    'do' => 'mute',
                    'days' => 7,
                    'id' => $id,
                    '_wpnonce' => $nonce,
                ], $base);
                $m30 = add_query_arg([
                    'action' => 'wpsn_notifications_action',
                    'do' => 'mute',
                    'days' => 30,
                    'id' => $id,
                    '_wpnonce' => $nonce,
                ], $base);
                echo '<a class="button" href="' . esc_url($m7) . '">' . esc_html__('Mute 7d', 'wp-seo-next') . '</a> ';
                echo '<a class="button" href="' . esc_url($m30) . '">' . esc_html__('Mute 30d', 'wp-seo-next') . '</a> ';
            } else {
                $un = add_query_arg([
                    'action' => 'wpsn_notifications_action',
                    'do' => 'unmute',
                    'id' => $id,
                    '_wpnonce' => $nonce,
                ], $base);
                echo '<a class="button" href="' . esc_url($un) . '">' . esc_html__('Unmute', 'wp-seo-next') . '</a> ';
            }

            $del = add_query_arg([
                'action' => 'wpsn_notifications_action',
                'do' => 'delete',
                'id' => $id,
                '_wpnonce' => $nonce,
            ], $base);
            echo '<a class="button" href="' . esc_url($del) . '" onclick="return confirm(\'" . esc_js(__("Delete this notification?", "wp-seo-next")) . "\');">' . esc_html__('Delete', 'wp-seo-next') . '</a>';

            echo '</td>';
            echo '</tr>';
        }

        echo '</tbody></table>';

        echo '<p class="description" style="margin-top:10px;">' . esc_html__('Tip: notifications are queued and sent by the WP SEO Next queue runner.', 'wp-seo-next') . '</p>';
    }

    /**
     * @param array<string,mixed> $meta
     * @return array{text:string,compare_url:string}
     */
    private function platformOpsNotificationPreview(array $meta): array
    {
        $parts = [];

        $preview = isset($meta['preview']) && is_array($meta['preview']) ? $meta['preview'] : [];
        if (!empty($preview['overall'])) {
            $parts[] = sanitize_text_field((string) $preview['overall']);
        }

        $checks = [];
        if (isset($preview['checks']) && is_array($preview['checks'])) {
            foreach (array_slice($preview['checks'], 0, 3) as $label) {
                $label = sanitize_text_field((string) $label);
                if ($label !== '') {
                    $checks[] = $label;
                }
            }
        }

        if (empty($checks)) {
            $flips = isset($meta['flips']) && is_array($meta['flips']) ? $meta['flips'] : [];
            foreach (array_slice($flips, 0, 3) as $flip) {
                if (!is_array($flip)) {
                    continue;
                }
                $label = sanitize_text_field((string) ($flip['label'] ?? ''));
                $to = strtoupper(sanitize_key((string) ($flip['to'] ?? 'warn')));
                if ($label !== '') {
                    $checks[] = $label . ' → ' . $to;
                }
            }
        }

        if (!empty($checks)) {
            $more = 0;
            $flips = isset($meta['flips']) && is_array($meta['flips']) ? $meta['flips'] : [];
            if (count($flips) > count($checks)) {
                $more = count($flips) - count($checks);
            }
            $checksText = implode(', ', $checks);
            if ($more > 0) {
                $checksText .= ' +' . $more;
            }
            $parts[] = sprintf(__('Checks: %s', 'wp-seo-next'), $checksText);
        }

        $diffSummary = isset($meta['diff_summary']) && is_array($meta['diff_summary']) ? $meta['diff_summary'] : [];
        if (!empty($diffSummary)) {
            $parts[] = sprintf(
                __('Regressions %1$d · Improvements %2$d · Changed %3$d', 'wp-seo-next'),
                (int) ($diffSummary['regressions'] ?? 0),
                (int) ($diffSummary['improvements'] ?? 0),
                (int) ($diffSummary['changed'] ?? 0)
            );
        }

        $baseId = sanitize_text_field((string) ($meta['previous_run_id'] ?? ''));
        $candidateId = sanitize_text_field((string) ($meta['candidate_run_id'] ?? ''));
        $compareUrl = '';
        if ($baseId !== '' && $candidateId !== '' && $baseId !== $candidateId) {
            $compareUrl = add_query_arg([
                'page' => 'wpsn-platform-ops',
                'platform_compare_base' => $baseId,
                'platform_compare_candidate' => $candidateId,
            ], admin_url('tools.php')) . '#wpsn-platform-ops-compare';
        }

        return [
            'text' => implode(' · ', array_filter($parts, static function ($part): bool {
                return trim((string) $part) !== '';
            })),
            'compare_url' => $compareUrl,
        ];
    }

    private function renderSettings(): void
    {
        $settings = $this->container->get('settings');
        $cfg = $settings->get('notifications', []);
        if (!is_array($cfg)) {
            $cfg = [];
        }

        $enabled = !empty($cfg['enabled']);
        $minSeverity = isset($cfg['min_severity']) ? sanitize_key((string) $cfg['min_severity']) : 'warn';
        if (!in_array($minSeverity, ['info', 'warn', 'critical'], true)) {
            $minSeverity = 'warn';
        }

        $defaultMute = isset($cfg['default_mute_days']) ? (int) $cfg['default_mute_days'] : 7;
        $defaultMute = max(1, min(365, $defaultMute));

        $channels = isset($cfg['channels']) && is_array($cfg['channels']) ? $cfg['channels'] : [];
        $emailEnabled = !empty($channels['email_enabled']);
        $emailRecipients = isset($channels['email_recipients']) ? (string) $channels['email_recipients'] : '';
        $slackEnabled = !empty($channels['slack_enabled']);
        $slackWebhook = isset($channels['slack_webhook']) ? (string) $channels['slack_webhook'] : '';
        $webhookEnabled = !empty($channels['webhook_enabled']);
        $webhookUrls = isset($channels['webhook_urls']) ? (string) $channels['webhook_urls'] : '';

        $sources = isset($cfg['sources']) && is_array($cfg['sources']) ? $cfg['sources'] : [];

        $integrations = isset($cfg['integrations']) && is_array($cfg['integrations']) ? $cfg['integrations'] : [];
        $intDriftMode = isset($integrations['drift_monitor']) && is_array($integrations['drift_monitor']) ? sanitize_key((string) ($integrations['drift_monitor']['mode'] ?? 'off')) : 'off';
        $intGscMode = isset($integrations['gsc_digest']) && is_array($integrations['gsc_digest']) ? sanitize_key((string) ($integrations['gsc_digest']['mode'] ?? 'off')) : 'off';
        if (!in_array($intDriftMode, ['off', 'inbox', 'route'], true)) {
            $intDriftMode = 'off';
        }
        if (!in_array($intGscMode, ['off', 'inbox', 'route'], true)) {
            $intGscMode = 'off';
        }

        $rules = isset($cfg['rules']) && is_array($cfg['rules']) ? $cfg['rules'] : [];
        $sendBudget = isset($rules['send_budget_per_hour']) ? (int) $rules['send_budget_per_hour'] : 30;
        $sendBudget = max(0, min(500, $sendBudget));

        $ruleSources = isset($rules['sources']) && is_array($rules['sources']) ? $rules['sources'] : [];

        $dig = isset($cfg['digests']) && is_array($cfg['digests']) ? $cfg['digests'] : [];
        $dailyEnabled = !empty($dig['daily_enabled']);
        $dailyHour = isset($dig['daily_hour_utc']) ? (int) $dig['daily_hour_utc'] : 9;
        $dailyHour = max(0, min(23, $dailyHour));

        $weeklyEnabled = !empty($dig['weekly_enabled']);
        $weeklyWday = isset($dig['weekly_weekday_utc']) ? (int) $dig['weekly_weekday_utc'] : 1;
        $weeklyWday = max(1, min(7, $weeklyWday));
        $weeklyHour = isset($dig['weekly_hour_utc']) ? (int) $dig['weekly_hour_utc'] : 9;
        $weeklyHour = max(0, min(23, $weeklyHour));

        $digestMaxItems = isset($dig['max_items']) ? (int) $dig['max_items'] : 25;
        $digestMaxItems = max(1, min(200, $digestMaxItems));

        $digestMinSev = isset($dig['min_severity']) ? sanitize_key((string) $dig['min_severity']) : $minSeverity;
        if (!in_array($digestMinSev, ['info', 'warn', 'critical'], true)) {
            $digestMinSev = $minSeverity;
        }

        $digestSendEmpty = !empty($dig['send_when_empty']);

        $nonce = wp_create_nonce('wpsn_notifications_save');

        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
        echo '<input type="hidden" name="action" value="wpsn_notifications_save" />';
        echo '<input type="hidden" name="_wpnonce" value="' . esc_attr($nonce) . '" />';

        echo '<table class="form-table" role="presentation">';

        echo '<tr><th scope="row">' . esc_html__('Enable notifications', 'wp-seo-next') . '</th><td>';
        echo '<label><input type="checkbox" name="notifications[enabled]" value="1" ' . checked($enabled, true, false) . ' /> ' . esc_html__('Capture + send notifications for critical events', 'wp-seo-next') . '</label>';
        echo '</td></tr>';

        echo '<tr><th scope="row">' . esc_html__('Minimum severity to send', 'wp-seo-next') . '</th><td>';
        echo '<select name="notifications[min_severity]">';
        foreach (['info' => 'INFO', 'warn' => 'WARN', 'critical' => 'CRITICAL'] as $k => $label) {
            echo '<option value="' . esc_attr($k) . '" ' . selected($minSeverity, $k, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select>';
        echo '<p class="description">' . esc_html__('Events below this severity will still be recorded in the Inbox, but not sent.', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        echo '<tr><th scope="row">' . esc_html__('Default mute days', 'wp-seo-next') . '</th><td>';
        echo '<input type="number" min="1" max="365" name="notifications[default_mute_days]" value="' . esc_attr((string) $defaultMute) . '" />';
        echo '<p class="description">' . esc_html__('Used by some sources to deduplicate repeated errors (for example, SaaS sync failures).', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        echo '</table>';

        echo '<h2>' . esc_html__('Integrations', 'wp-seo-next') . '</h2>';
        echo '<table class="form-table" role="presentation">';

        $modeOptions = [
            'off' => __('Off', 'wp-seo-next'),
            'inbox' => __('Inbox only (do not send)', 'wp-seo-next'),
            'route' => __('Route via Notification Center (disable legacy sends)', 'wp-seo-next'),
        ];

        echo '<tr><th scope="row">' . esc_html__('Drift Monitor', 'wp-seo-next') . '</th><td>';
        echo '<select name="notifications[integrations][drift_monitor][mode]">';
        foreach ($modeOptions as $k => $label) {
            echo '<option value="' . esc_attr($k) . '" ' . selected($intDriftMode, $k, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select>';
        echo '<p class="description">' . esc_html__('When set to “Route”, Drift Monitor will stop sending its own email/Slack/webhook notifications (to avoid duplicates) and rely on this Notification Center instead.', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        echo '<tr><th scope="row">' . esc_html__('GSC Rank Tracking digest', 'wp-seo-next') . '</th><td>';
        echo '<select name="notifications[integrations][gsc_digest][mode]">';
        foreach ($modeOptions as $k => $label) {
            echo '<option value="' . esc_attr($k) . '" ' . selected($intGscMode, $k, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select>';
        echo '<p class="description">' . esc_html__('When set to “Route”, the legacy digest email/Slack sending in Search Console → Rank Tracking will be skipped and delivery will be handled here.', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        echo '</table>';

        echo '<h2>' . esc_html__('Channels', 'wp-seo-next') . '</h2>';
        echo '<table class="form-table" role="presentation">';

        echo '<tr><th scope="row">' . esc_html__('Email', 'wp-seo-next') . '</th><td>';
        echo '<label><input type="checkbox" name="notifications[channels][email_enabled]" value="1" ' . checked($emailEnabled, true, false) . ' /> ' . esc_html__('Send email', 'wp-seo-next') . '</label><br />';
        echo '<input type="text" name="notifications[channels][email_recipients]" value="' . esc_attr($emailRecipients) . '" style="width:520px" placeholder="admin@example.com, seo@example.com" />';
        echo '<p class="description">' . esc_html__('If empty, site admin email is used.', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        echo '<tr><th scope="row">' . esc_html__('Slack', 'wp-seo-next') . '</th><td>';
        echo '<label><input type="checkbox" name="notifications[channels][slack_enabled]" value="1" ' . checked($slackEnabled, true, false) . ' /> ' . esc_html__('Send Slack notifications', 'wp-seo-next') . '</label><br />';
        echo '<input type="text" name="notifications[channels][slack_webhook]" value="' . esc_attr($slackWebhook) . '" style="width:520px" placeholder="https://hooks.slack.com/services/..." />';
        echo '<p class="description">' . esc_html__('Slack is an external request and will be blocked by Local-only mode.', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        echo '<tr><th scope="row">' . esc_html__('Webhook', 'wp-seo-next') . '</th><td>';
        echo '<label><input type="checkbox" name="notifications[channels][webhook_enabled]" value="1" ' . checked($webhookEnabled, true, false) . ' /> ' . esc_html__('Send webhooks (JSON POST)', 'wp-seo-next') . '</label><br />';
        echo '<textarea name="notifications[channels][webhook_urls]" style="width:520px; height:90px;" placeholder="https://example.com/webhook\nhttps://hooks.zapier.com/...">' . esc_textarea($webhookUrls) . '</textarea>';
        echo '<p class="description">' . esc_html__('One URL per line. External requests will be blocked by Local-only mode.', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        echo '</table>';

        echo '<h2>' . esc_html__('Sources', 'wp-seo-next') . '</h2>';
        echo '<table class="form-table" role="presentation">';

        $srcFields = [
            'head_diff' => __('Head output changes after update', 'wp-seo-next'),
            'queue_failed' => __('Queue job failures', 'wp-seo-next'),
            'audit_critical' => __('Audit scan critical issues', 'wp-seo-next'),
            'quality_gate' => __('Quality Gate failures (sitemap/robots/head/queue)', 'wp-seo-next'),
            'saas_sync_failed' => __('SaaS Sync failures', 'wp-seo-next'),
            'serp_gap_failed' => __('SERP Gap run failed', 'wp-seo-next'),
            'platform_ops' => __('Platform backend flips (scheduled Platform Ops probe)', 'wp-seo-next'),
        ];

        foreach ($srcFields as $k => $label) {
            $on = !empty($sources[$k]);
            echo '<tr><th scope="row">' . esc_html($label) . '</th><td>';
            echo '<label><input type="checkbox" name="notifications[sources][' . esc_attr($k) . ']" value="1" ' . checked($on, true, false) . ' /> ' . esc_html__('Enabled', 'wp-seo-next') . '</label>';
            echo '</td></tr>';
        }

        echo '</table>';

        echo '<h2>' . esc_html__('Routing rules', 'wp-seo-next') . '</h2>';
        echo '<p class="description">' . esc_html__('Optional per-source overrides: minimum severity to send, and “dedupe window” to avoid sending the same class of event too often.', 'wp-seo-next') . '</p>';
        echo '<table class="form-table" role="presentation">';

        echo '<tr><th scope="row">' . esc_html__('Send budget (per hour)', 'wp-seo-next') . '</th><td>';
        echo '<input type="number" min="0" max="500" name="notifications[rules][send_budget_per_hour]" value="' . esc_attr((string) $sendBudget) . '" />';
        echo '<p class="description">' . esc_html__('0 = unlimited. Best-effort guard to prevent notification storms (applies to all channels combined).', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        $ruleFields = [
            'head_diff' => __('Head diff after update', 'wp-seo-next'),
            'queue' => __('Queue failures', 'wp-seo-next'),
            'audit' => __('Audit critical issues', 'wp-seo-next'),
            'quality_gate' => __('Quality Gate', 'wp-seo-next'),
            'saas_sync' => __('SaaS Sync failures', 'wp-seo-next'),
            'serp_gap' => __('SERP Gap failures', 'wp-seo-next'),
            'platform_ops' => __('Platform backend flips', 'wp-seo-next'),
            'drift_monitor' => __('Drift Monitor (integration)', 'wp-seo-next'),
            'search_console' => __('Search Console (integration)', 'wp-seo-next'),
            'test' => __('Test', 'wp-seo-next'),
        ];

        foreach ($ruleFields as $srcKey => $label) {
            $rs = isset($ruleSources[$srcKey]) && is_array($ruleSources[$srcKey]) ? $ruleSources[$srcKey] : [];
            $rMin = isset($rs['min_severity']) ? sanitize_key((string) $rs['min_severity']) : $minSeverity;
            if (!in_array($rMin, ['info', 'warn', 'critical'], true)) {
                $rMin = $minSeverity;
            }
            $rWin = isset($rs['dedupe_window_minutes']) ? (int) $rs['dedupe_window_minutes'] : 0;
            $rWin = max(0, min(10080, $rWin));

            echo '<tr><th scope="row">' . esc_html($label) . '</th><td>';
            echo '<label style="margin-right:12px;">' . esc_html__('Min severity:', 'wp-seo-next') . ' ';
            echo '<select name="notifications[rules][sources][' . esc_attr($srcKey) . '][min_severity]">';
            foreach (['info' => 'INFO', 'warn' => 'WARN', 'critical' => 'CRITICAL'] as $k => $lbl) {
                echo '<option value="' . esc_attr($k) . '" ' . selected($rMin, $k, false) . '>' . esc_html($lbl) . '</option>';
            }
            echo '</select></label> ';

            echo '<label>' . esc_html__('Dedupe window (minutes):', 'wp-seo-next') . ' ';
            echo '<input type="number" min="0" max="10080" name="notifications[rules][sources][' . esc_attr($srcKey) . '][dedupe_window_minutes]" value="' . esc_attr((string) $rWin) . '" style="width:120px;" />';
            echo '</label>';

            echo '<p class="description">' . esc_html__('0 = disabled. If > 0, similar events will be sent at most once per window (events are still recorded in the Inbox).', 'wp-seo-next') . '</p>';
            echo '</td></tr>';
        }

        echo '</table>';

        echo '<h2>' . esc_html__('Digests', 'wp-seo-next') . '</h2>';
        echo '<p class="description">' . esc_html__('Optional daily/weekly summaries of open notifications (uses the same channels as above).', 'wp-seo-next') . '</p>';
        echo '<table class="form-table" role="presentation">';

        echo '<tr><th scope="row">' . esc_html__('Daily digest', 'wp-seo-next') . '</th><td>';
        echo '<label><input type="checkbox" name="notifications[digests][daily_enabled]" value="1" ' . checked($dailyEnabled, true, false) . ' /> ' . esc_html__('Enable', 'wp-seo-next') . '</label><br />';
        echo '<label>' . esc_html__('Send hour (UTC):', 'wp-seo-next') . ' ';
        echo '<input type="number" min="0" max="23" name="notifications[digests][daily_hour_utc]" value="' . esc_attr((string) $dailyHour) . '" style="width:80px;" />';
        echo '</label>';
        echo '</td></tr>';

        echo '<tr><th scope="row">' . esc_html__('Weekly digest', 'wp-seo-next') . '</th><td>';
        echo '<label><input type="checkbox" name="notifications[digests][weekly_enabled]" value="1" ' . checked($weeklyEnabled, true, false) . ' /> ' . esc_html__('Enable', 'wp-seo-next') . '</label><br />';
        echo '<label>' . esc_html__('Weekday (UTC):', 'wp-seo-next') . ' ';
        echo '<select name="notifications[digests][weekly_weekday_utc]">';
        $wlabels = [1 => __('Mon', 'wp-seo-next'), 2 => __('Tue', 'wp-seo-next'), 3 => __('Wed', 'wp-seo-next'), 4 => __('Thu', 'wp-seo-next'), 5 => __('Fri', 'wp-seo-next'), 6 => __('Sat', 'wp-seo-next'), 7 => __('Sun', 'wp-seo-next')];
        foreach ($wlabels as $wk => $wl) {
            echo '<option value="' . esc_attr((string) $wk) . '" ' . selected($weeklyWday, $wk, false) . '>' . esc_html($wl) . '</option>';
        }
        echo '</select></label> ';
        echo '<label>' . esc_html__('Send hour (UTC):', 'wp-seo-next') . ' ';
        echo '<input type="number" min="0" max="23" name="notifications[digests][weekly_hour_utc]" value="' . esc_attr((string) $weeklyHour) . '" style="width:80px;" />';
        echo '</label>';
        echo '</td></tr>';

        echo '<tr><th scope="row">' . esc_html__('Digest content', 'wp-seo-next') . '</th><td>';
        echo '<label>' . esc_html__('Max items:', 'wp-seo-next') . ' ';
        echo '<input type="number" min="1" max="200" name="notifications[digests][max_items]" value="' . esc_attr((string) $digestMaxItems) . '" style="width:90px;" />';
        echo '</label><br />';

        echo '<label>' . esc_html__('Min severity to include:', 'wp-seo-next') . ' ';
        echo '<select name="notifications[digests][min_severity]">';
        foreach (['info' => 'INFO', 'warn' => 'WARN', 'critical' => 'CRITICAL'] as $k => $lbl) {
            echo '<option value="' . esc_attr($k) . '" ' . selected($digestMinSev, $k, false) . '>' . esc_html($lbl) . '</option>';
        }
        echo '</select></label><br />';

        echo '<label><input type="checkbox" name="notifications[digests][send_when_empty]" value="1" ' . checked($digestSendEmpty, true, false) . ' /> ' . esc_html__('Send even if there are 0 open items', 'wp-seo-next') . '</label>';

        echo '<p class="description">' . esc_html__('Digests include only “Open” items (not acknowledged, not muted).', 'wp-seo-next') . '</p>';
        echo '</td></tr>';

        echo '</table>';

        submit_button(__('Save', 'wp-seo-next'));

        echo '</form>';

        // Test event.
        echo '<hr />';
        echo '<h2>' . esc_html__('Test', 'wp-seo-next') . '</h2>';
        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
        echo '<input type="hidden" name="action" value="wpsn_notifications_test" />';
        echo '<input type="hidden" name="_wpnonce" value="' . esc_attr(wp_create_nonce('wpsn_notifications_test')) . '" />';
        submit_button(__('Queue test notification', 'wp-seo-next'), 'secondary');
        echo '</form>';

        echo '<p class="description">' . esc_html__('The test will create an INFO event and enqueue delivery via the queue runner.', 'wp-seo-next') . '</p>';
    }

    public function handleSave(): void
    {
        Security::requireCapability(Capabilities::CAP_TOOLS);
        Security::checkAdminNonce('wpsn_notifications_save');

        $posted = isset($_POST['notifications']) ? (array) wp_unslash($_POST['notifications']) : [];

        $repo = new SettingsRepository();
        $all = $repo->getAll();
        $all['notifications'] = $posted;
        $repo->update($all);

        wp_safe_redirect($this->pageUrl(['tab' => 'settings', 'wpsn_notice' => 'saved']));
        exit;
    }

    public function handleAction(): void
    {
        Security::requireCapability(Capabilities::CAP_TOOLS);
        Security::checkAdminNonce('wpsn_notifications_action');

        $do = isset($_GET['do']) ? sanitize_key((string) wp_unslash($_GET['do'])) : '';
        $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;

        if ($id <= 0) {
            wp_safe_redirect($this->pageUrl(['tab' => 'inbox']));
            exit;
        }

        if ($do === 'ack') {
            $this->repo->acknowledge([$id]);
        } elseif ($do === 'mute') {
            $days = isset($_GET['days']) ? (int) $_GET['days'] : 7;
            $this->repo->mute([$id], $days);
        } elseif ($do === 'unmute') {
            $this->repo->unmute([$id]);
        } elseif ($do === 'delete') {
            $this->repo->delete([$id]);
        }

        wp_safe_redirect($this->pageUrl(['tab' => 'inbox', 'wpsn_notice' => 'done']));
        exit;
    }

    public function handleTest(): void
    {
        Security::requireCapability(Capabilities::CAP_TOOLS);
        Security::checkAdminNonce('wpsn_notifications_test');

        $jobs = new NotificationsJobs($this->container);
        $jobs->createTestEvent();

        wp_safe_redirect($this->pageUrl(['tab' => 'settings', 'wpsn_notice' => 'test_queued']));
        exit;
    }
}
