<?php

namespace WPSN\Modules\Stability\Admin;

use WPSN\Core\Capabilities;
use WPSN\Core\Http\HttpClient;
use WPSN\Core\Security;
use WPSN\Core\Settings;

defined('ABSPATH') || exit;

final class CanonicalMatrixAdmin
{
    private const PAGE_SLUG = 'wpsn-canonical-matrix';

    private Settings $settings;

    public function __construct(Settings $settings)
    {
        $this->settings = $settings;
    }

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

    public function addMenu(): void
    {
        add_management_page(
            __('SEO Canonical Matrix', 'wp-seo-next'),
            __('Canonical Matrix', 'wp-seo-next'),
            Capabilities::CAP_TOOLS,
            self::PAGE_SLUG,
            [$this, 'render']
        );
    }

    /**
     * @return array<int,array{label:string,url:string,note:string}>
     */
    private function buildTestUrls(): array
    {
        $out = [];

        $home = (string) home_url('/');
        $out[] = ['label' => __('Home', 'wp-seo-next'), 'url' => $home, 'note' => ''];

        // Published post.
        $posts = get_posts([
            'post_type' => 'post',
            'post_status' => 'publish',
            'numberposts' => 1,
            'orderby' => 'date',
            'order' => 'DESC',
            'fields' => 'ids',
        ]);
        if (!empty($posts) && is_array($posts)) {
            $pid = (int) $posts[0];
            $purl = get_permalink($pid);
            if (is_string($purl) && $purl !== '') {
                $out[] = ['label' => __('Post', 'wp-seo-next'), 'url' => $purl, 'note' => ''];
            }
        }

        // Category term.
        $terms = get_terms([
            'taxonomy' => 'category',
            'hide_empty' => false,
            'number' => 1,
        ]);
        if (!is_wp_error($terms) && !empty($terms) && is_array($terms)) {
            $t = $terms[0];
            if ($t instanceof \WP_Term) {
                $turl = get_term_link($t);
                if (is_string($turl) && $turl !== '') {
                    $out[] = ['label' => __('Category', 'wp-seo-next'), 'url' => $turl, 'note' => ''];
                }
            }
        }

        // Search.
        if (function_exists('get_search_link')) {
            $surl = get_search_link('wpsn');
            if (is_string($surl) && $surl !== '') {
                $out[] = ['label' => __('Search', 'wp-seo-next'), 'url' => $surl, 'note' => 'q=wpsn'];
            }
        }

        // Post type archive.
        if (function_exists('get_post_type_archive_link')) {
            $aurl = get_post_type_archive_link('post');
            if (is_string($aurl) && $aurl !== '') {
                $out[] = ['label' => __('Archive', 'wp-seo-next'), 'url' => $aurl, 'note' => 'post type archive'];
            }
        }

        // UTM variants (to confirm canonical normalization removes tracking args).
        $utm = [
            'utm_source' => 'wpsn_test',
            'utm_medium' => 'canonical_matrix',
            'gclid' => 'test',
        ];

        $withUtm = [];
        foreach ($out as $row) {
            $u = add_query_arg($utm, $row['url']);
            $withUtm[] = [
                'label' => $row['label'] . ' + UTM',
                'url' => (string) $u,
                'note' => 'utm_source/utm_medium/gclid',
            ];
        }

        // Keep matrix small.
        $out = array_merge($out, array_slice($withUtm, 0, 5));

        return $out;
    }

    private function extractCanonical(string $html): string
    {
        if ($html === '') {
            return '';
        }

        if (preg_match('~<link[^>]+rel=["\']canonical["\'][^>]*>~i', $html, $m)) {
            $tag = $m[0];
            if (preg_match('~href=["\']([^"\']+)["\']~i', $tag, $mm)) {
                return trim((string) $mm[1]);
            }
        }

        return '';
    }

    private function extractRobots(string $html): string
    {
        if ($html === '') {
            return '';
        }

        if (preg_match('~<meta[^>]+name=["\']robots["\'][^>]*>~i', $html, $m)) {
            $tag = $m[0];
            if (preg_match('~content=["\']([^"\']+)["\']~i', $tag, $mm)) {
                return trim((string) $mm[1]);
            }
        }

        return '';
    }

    /**
     * @return array<int,array<string,mixed>>
     */
    private function runMatrix(): array
    {
        $tests = $this->buildTestUrls();
        $rows = [];

        foreach ($tests as $t) {
            $url = (string) $t['url'];
            $start = microtime(true);

            $resp = HttpClient::get($url, [
                'timeout' => 12,
                'redirection' => 3,
                'headers' => [
                    'Accept' => 'text/html',
                ],
            ], [
                'service' => 'loopback',
                'context' => 'canonical_matrix',
                'retries' => 0,
            ]);

            $ms = (int) round((microtime(true) - $start) * 1000);

            if (is_wp_error($resp)) {
                $rows[] = [
                    'label' => (string) $t['label'],
                    'url' => $url,
                    'code' => 'ERR',
                    'canonical' => '',
                    'robots' => '',
                    'note' => (string) $t['note'],
                    'time_ms' => $ms,
                    'error' => $resp->get_error_message(),
                ];
                continue;
            }

            $code = (int) wp_remote_retrieve_response_code($resp);
            $body = (string) wp_remote_retrieve_body($resp);

            // Limit parsing size.
            if (strlen($body) > 250000) {
                $body = substr($body, 0, 250000);
            }

            $canonical = $this->extractCanonical($body);
            $robots = $this->extractRobots($body);

            $rows[] = [
                'label' => (string) $t['label'],
                'url' => $url,
                'code' => $code,
                'canonical' => $canonical,
                'robots' => $robots,
                'note' => (string) $t['note'],
                'time_ms' => $ms,
                'error' => '',
            ];
        }

        return $rows;
    }

    public function render(): void
    {
        Security::requireCapability(Capabilities::CAP_TOOLS);

        $localOnly = (bool) $this->settings->get('privacy.local_only', false);

        echo '<div class="wrap">';
        echo '<h1>' . esc_html__('SEO Canonical Matrix', 'wp-seo-next') . '</h1>';
        echo '<p class="description">' . esc_html__('Runs a small loopback matrix to verify canonical + robots meta on typical page types (and UTM variants).', 'wp-seo-next') . '</p>';

        if ($localOnly) {
            echo '<div class="notice notice-warning"><p><strong>' . esc_html__('Local-only mode is enabled:', 'wp-seo-next') . '</strong> ' . esc_html__('loopback HTTP requests may be blocked.', 'wp-seo-next') . '</p></div>';
        }

        $rows = null;

        if (isset($_POST['wpsn_run_matrix'])) {
            Security::checkAdminNonce('wpsn_run_canonical_matrix', '_wpnonce');
            $rows = $this->runMatrix();
        }

        echo '<form method="post" style="margin: 12px 0;">';
        wp_nonce_field('wpsn_run_canonical_matrix');
        echo '<input type="hidden" name="wpsn_run_matrix" value="1" />';
        submit_button(__('Run matrix now', 'wp-seo-next'), 'primary', 'submit', false);
        echo '</form>';

        if (is_array($rows)) {
            echo '<h2>' . esc_html__('Results', 'wp-seo-next') . '</h2>';
            echo '<table class="widefat striped" style="max-width: 1400px;">';
            echo '<thead><tr>';
            echo '<th style="width:180px;">' . esc_html__('Case', 'wp-seo-next') . '</th>';
            echo '<th>' . esc_html__('Requested URL', 'wp-seo-next') . '</th>';
            echo '<th style="width:70px;">' . esc_html__('HTTP', 'wp-seo-next') . '</th>';
            echo '<th>' . esc_html__('Canonical', 'wp-seo-next') . '</th>';
            echo '<th style="width:200px;">' . esc_html__('Robots meta', 'wp-seo-next') . '</th>';
            echo '<th style="width:90px;">' . esc_html__('Time', 'wp-seo-next') . '</th>';
            echo '</tr></thead><tbody>';

            foreach ($rows as $r) {
                $code = $r['code'];
                $err = (string) ($r['error'] ?? '');
                $canonical = (string) ($r['canonical'] ?? '');

                // Highlight canonical mismatch.
                $mismatch = ($canonical !== '' && $canonical !== (string) $r['url']);

                echo '<tr>';
                echo '<td><strong>' . esc_html((string) ($r['label'] ?? '')) . '</strong><br/><span class="description">' . esc_html((string) ($r['note'] ?? '')) . '</span></td>';
                echo '<td><code>' . esc_html((string) ($r['url'] ?? '')) . '</code>';
                if ($err !== '') {
                    echo '<br/><span class="description" style="color:#b32d2e;">' . esc_html($err) . '</span>';
                }
                echo '</td>';
                echo '<td><code>' . esc_html((string) $code) . '</code></td>';

                if ($canonical !== '') {
                    echo '<td>' . ($mismatch ? '<strong>' : '') . '<code>' . esc_html($canonical) . '</code>' . ($mismatch ? '</strong>' : '') . '</td>';
                } else {
                    echo '<td>—</td>';
                }

                $robots = (string) ($r['robots'] ?? '');
                echo '<td>' . ($robots !== '' ? '<code>' . esc_html($robots) . '</code>' : '—') . '</td>';

                $ms = (int) ($r['time_ms'] ?? 0);
                echo '<td>' . esc_html((string) $ms) . ' ms</td>';

                echo '</tr>';
            }

            echo '</tbody></table>';
            echo '<p class="description" style="margin-top:10px;">' . esc_html__('Note: canonical mismatches are normal for UTM variants. The goal is to ensure tracking parameters deduplicate to the clean canonical.', 'wp-seo-next') . '</p>';
        }

        echo '</div>';
    }
}
