<?php

namespace WPSN\Modules\Diagnostics;

use WPSN\Core\Container;
use WPSN\Core\Settings;
use WPSN\Core\Queue\QueueInterface;

defined('ABSPATH') || exit;

/**
 * Adds WordPress Site Health tests for common WP SEO Next performance & reliability issues.
 *
 * @since 0.50.0
 */
final class SiteHealthTests
{
    private Container $container;
    private Settings $settings;
    private QueueInterface $queue;

    public function __construct(Container $container)
    {
        $this->container = $container;
        /** @var Settings $settings */
        $settings = $container->get('settings');
        $this->settings = $settings;
        /** @var QueueInterface $queue */
        $queue = $container->get('queue');
        $this->queue = $queue;
    }

    public function register(): void
    {
        add_filter('site_status_tests', [$this, 'addTests']);
        add_filter('debug_information', [$this, 'addDebugInfo']);
    }

    /**
     * @param array<string,mixed> $tests
     * @return array<string,mixed>
     */
    public function addTests(array $tests): array
    {
        if (!isset($tests['direct']) || !is_array($tests['direct'])) {
            $tests['direct'] = [];
        }

        $tests['direct']['wpsn_queue_backlog'] = [
            'label' => __('WP SEO Next: Queue backlog', 'wp-seo-next'),
            'test' => [$this, 'testQueueBacklog'],
        ];

        $tests['direct']['wpsn_queue_failed'] = [
            'label' => __('WP SEO Next: Failed jobs', 'wp-seo-next'),
            'test' => [$this, 'testQueueFailedJobs'],
        ];

        $tests['direct']['wpsn_queue_cron'] = [
            'label' => __('WP SEO Next: Queue scheduling / WP-Cron', 'wp-seo-next'),
            'test' => [$this, 'testQueueCron'],
        ];

        $tests['direct']['wpsn_tables_size'] = [
            'label' => __('WP SEO Next: Plugin tables size', 'wp-seo-next'),
            'test' => [$this, 'testTablesSize'],
        ];

        return $tests;
    }

    /**
     * @return array<string,mixed>
     */
    public function testQueueBacklog(): array
    {
        $counts = $this->queue->counts();
        $pending = (int) ($counts['pending'] ?? 0);
        $running = (int) ($counts['running'] ?? 0);

        $total = $pending + $running;

        $status = 'good';
        $label = __('Queue backlog is under control.', 'wp-seo-next');
        $desc = sprintf(
            /* translators: 1: pending jobs 2: running jobs */
            __('Pending: %1$d, running: %2$d.', 'wp-seo-next'),
            $pending,
            $running
        );

        if ($pending >= 500) {
            $status = 'critical';
            $label = __('Queue backlog is large.', 'wp-seo-next');
        } elseif ($pending >= 100) {
            $status = 'recommended';
            $label = __('Queue backlog is growing.', 'wp-seo-next');
        }

        $actions = '';
        $bulkUrl = admin_url('tools.php?page=wpsn-seo-bulk&view=jobs');
        $actions = '<p><a href="' . esc_url($bulkUrl) . '">' . esc_html__('Open Bulk / Jobs', 'wp-seo-next') . '</a></p>';

        return [
            'label' => $label,
            'status' => $status,
            'badge' => [
                'label' => __('WP SEO Next', 'wp-seo-next'),
                'color' => 'blue',
            ],
            'description' => '<p>' . esc_html($desc) . '</p>',
            'actions' => $actions,
            'test' => 'wpsn_queue_backlog',
        ];
    }

    /**
     * @return array<string,mixed>
     */
    public function testQueueFailedJobs(): array
    {
        $counts = $this->queue->counts();
        $failed = (int) ($counts['failed'] ?? 0);

        $status = 'good';
        $label = __('No failed jobs detected.', 'wp-seo-next');
        $desc = sprintf(
            /* translators: %d: failed jobs */
            __('Failed jobs: %d.', 'wp-seo-next'),
            $failed
        );

        if ($failed > 20) {
            $status = 'critical';
            $label = __('Many failed jobs detected.', 'wp-seo-next');
        } elseif ($failed > 0) {
            $status = 'recommended';
            $label = __('Some failed jobs detected.', 'wp-seo-next');
        }

        $actions = '';
        if ($failed > 0) {
            $url = admin_url('tools.php?page=wpsn-seo-bulk&view=failed');
            $actions = '<p><a href="' . esc_url($url) . '">' . esc_html__('Open Failed jobs', 'wp-seo-next') . '</a></p>';
        }

        return [
            'label' => $label,
            'status' => $status,
            'badge' => [
                'label' => __('WP SEO Next', 'wp-seo-next'),
                'color' => 'blue',
            ],
            'description' => '<p>' . esc_html($desc) . '</p>',
            'actions' => $actions,
            'test' => 'wpsn_queue_failed',
        ];
    }

    /**
     * @return array<string,mixed>
     */
    public function testQueueCron(): array
    {
        $mode = (string) $this->settings->get('performance.queue.mode', 'manual');
        $mode = $mode ?: 'manual';

        $pending = (int) (($this->queue->counts()['pending'] ?? 0));

        $cronDisabled = defined('DISABLE_WP_CRON') && DISABLE_WP_CRON;

        $next = wp_next_scheduled('wpsn_queue_tick');
        $status = 'good';
        $label = __('Queue scheduling looks OK.', 'wp-seo-next');

        $bits = [];
        $bits[] = sprintf(__('Mode: %s.', 'wp-seo-next'), $mode);
        if ($cronDisabled) {
            $bits[] = __('DISABLE_WP_CRON is enabled.', 'wp-seo-next');
        }
        if ($next) {
            $bits[] = sprintf(__('Next tick: %s.', 'wp-seo-next'), gmdate('Y-m-d H:i:s', (int) $next) . ' UTC');
        } else {
            $bits[] = __('Next tick: not scheduled.', 'wp-seo-next');
        }

        if ($mode === 'auto') {
            if ($cronDisabled) {
                $status = 'recommended';
                $label = __('WP-Cron is disabled while queue is set to auto.', 'wp-seo-next');
            }
            if (!$next) {
                $status = 'critical';
                $label = __('Queue tick is not scheduled.', 'wp-seo-next');
            }
        } elseif ($mode === 'direct') {
            // Direct mode intentionally does NOT schedule a WP-Cron tick.
            if ($pending > 0) {
                $status = 'recommended';
                $label = __('Queue is in direct mode with pending jobs.', 'wp-seo-next');
            } else {
                $status = 'good';
                $label = __('Direct queue mode is active.', 'wp-seo-next');
            }
        } else {
            // Manual mode with pending jobs → likely stuck.
            if ($pending > 0) {
                $status = 'recommended';
                $label = __('Queue is in manual mode with pending jobs.', 'wp-seo-next');
            }
        }

        $actions = '';
        $settingsUrl = admin_url('admin.php?page=wp-seo-next&tab=performance');
        $actions = '<p><a href="' . esc_url($settingsUrl) . '">' . esc_html__('Open Performance settings', 'wp-seo-next') . '</a></p>';

        if ($mode === 'auto' && $cronDisabled) {
            $actions .= '<p>' . esc_html__('If you run a real server cron for wp-cron.php, this may be OK. Otherwise, enable WP-Cron, switch queue mode to “direct”, or switch queue mode to “manual” and run WP-CLI in your deploy scripts.', 'wp-seo-next') . '</p>';
        }

        return [
            'label' => $label,
            'status' => $status,
            'badge' => [
                'label' => __('WP SEO Next', 'wp-seo-next'),
                'color' => 'blue',
            ],
            'description' => '<p>' . esc_html(implode(' ', $bits)) . '</p>',
            'actions' => $actions,
            'test' => 'wpsn_queue_cron',
        ];
    }

    /**
     * @return array<string,mixed>
     */
    public function testTablesSize(): array
    {
        global $wpdb;

        $tables = $this->getPluginTables();
        $totalBytes = 0;
        $largest = ['name' => '', 'bytes' => 0];

        foreach ($tables as $t) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            $row = $wpdb->get_row($wpdb->prepare('SHOW TABLE STATUS LIKE %s', $t), ARRAY_A);
            if (!is_array($row)) {
                continue;
            }
            $bytes = (int) ($row['Data_length'] ?? 0) + (int) ($row['Index_length'] ?? 0);
            $totalBytes += max(0, $bytes);
            if ($bytes > (int) $largest['bytes']) {
                $largest = ['name' => (string) $t, 'bytes' => $bytes];
            }
        }

        $totalMb = round($totalBytes / 1024 / 1024, 1);
        $largestMb = round(((int) $largest['bytes']) / 1024 / 1024, 1);

        $status = 'good';
        $label = __('Plugin tables size looks OK.', 'wp-seo-next');
        if ($totalMb >= 250) {
            $status = 'critical';
            $label = __('Plugin tables are very large.', 'wp-seo-next');
        } elseif ($totalMb >= 80) {
            $status = 'recommended';
            $label = __('Plugin tables are growing.', 'wp-seo-next');
        }

        $desc = sprintf(
            /* translators: 1: total size MB 2: largest table name 3: largest size MB */
            __('Total size: %1$s MB. Largest: %2$s (%3$s MB).', 'wp-seo-next'),
            (string) $totalMb,
            $largest['name'] ? (string) $largest['name'] : __('n/a', 'wp-seo-next'),
            (string) $largestMb
        );

        $actions = '<p><a href="' . esc_url(admin_url('tools.php?page=wpsn-diagnostics')) . '">' . esc_html__('Open Diagnostics', 'wp-seo-next') . '</a> · ';
        $actions .= '<a href="' . esc_url(admin_url('tools.php?page=wpsn-activity-log')) . '">' . esc_html__('Activity Log', 'wp-seo-next') . '</a></p>';

        return [
            'label' => $label,
            'status' => $status,
            'badge' => [
                'label' => __('WP SEO Next', 'wp-seo-next'),
                'color' => 'blue',
            ],
            'description' => '<p>' . esc_html($desc) . '</p>',
            'actions' => $actions,
            'test' => 'wpsn_tables_size',
        ];
    }

    /**
     * @return array<int,string>
     */
    private function getPluginTables(): array
    {
        global $wpdb;

        $list = [
            $wpdb->prefix . 'wpsn_queue_jobs',
            $wpdb->prefix . 'wpsn_activity_log',
            $wpdb->prefix . 'wpsn_external_log',
            $wpdb->prefix . 'wpsn_policy_log',
            $wpdb->prefix . 'wpsn_audit_scans',
            $wpdb->prefix . 'wpsn_audit_scan_issues',
            $wpdb->prefix . 'wpsn_audit_issues',
            $wpdb->prefix . 'wpsn_schema_scans',
            $wpdb->prefix . 'wpsn_schema_issues',
            $wpdb->prefix . 'wpsn_gsc_pages',
            $wpdb->prefix . 'wpsn_gsc_query_pages',
            $wpdb->prefix . 'wpsn_gsc_daily_pages',
            $wpdb->prefix . 'wpsn_gsc_daily_query_pages',
            $wpdb->prefix . 'wpsn_gsc_alerts',
            $wpdb->prefix . 'wpsn_il_suggestions',
            $wpdb->prefix . 'wpsn_il_inlinks',
            $wpdb->prefix . 'wpsn_ai_calls',
            $wpdb->prefix . 'wpsn_ai_credits_ledger',
        ];

        return array_values(array_unique($list));
    }

    /**
     * @param array<string,mixed> $info
     * @return array<string,mixed>
     */
    public function addDebugInfo(array $info): array
    {
        $counts = $this->queue->counts();

        $info['wpsn'] = [
            'label' => 'WP SEO Next',
            'fields' => [
                'Version' => [
                    'label' => 'Version',
                    'value' => defined('WPSN_VERSION') ? WPSN_VERSION : '',
                ],
                'Queue mode' => [
                    'label' => 'Queue mode',
                    'value' => (string) $this->settings->get('performance.queue.mode', 'manual'),
                ],
                'Queue counts' => [
                    'label' => 'Queue counts',
                    'value' => 'pending=' . (int) ($counts['pending'] ?? 0) . ', running=' . (int) ($counts['running'] ?? 0) . ', failed=' . (int) ($counts['failed'] ?? 0),
                ],
                'Next queue tick' => [
                    'label' => 'Next queue tick',
                    'value' => ($t = wp_next_scheduled('wpsn_queue_tick')) ? (gmdate('Y-m-d H:i:s', (int) $t) . ' UTC') : 'not scheduled',
                ],
            ],
        ];

        return $info;
    }
}
