<?php

namespace WPSN\Modules\Analysis\Admin;

use WPSN\Core\Capabilities;
use WPSN\Core\Container;
use WPSN\SEO\MetaKeys;

defined('ABSPATH') || exit;

/**
 * Admin UX for Stage 40 (On-page analysis):
 *
 * - Post list: sortable column + score filter.
 * - Tools report: Low score pages list.
 */
final class AnalysisAdmin
{
    public const PAGE_SLUG = 'wpsn-analysis-report';
    private const PER_PAGE_OPTION = 'wpsn_analysis_report_per_page';

    private Container $container;
    private ?string $hookSuffix = null;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function register(): void
    {
        // Post list table: sortable column + filtering.
        add_action('init', [$this, 'registerSortableColumns'], 20);
        add_action('restrict_manage_posts', [$this, 'renderScoreFilter']);
        add_action('pre_get_posts', [$this, 'handlePostsListQuery']);
        add_filter('posts_clauses', [$this, 'filterPostsClausesForScoreOrder'], 10, 2);

        // Tools report.
        add_action('admin_menu', [$this, 'addMenu']);
        add_filter('set-screen-option', [$this, 'setScreenOption'], 10, 3);
    }

    public function addMenu(): void
    {
        $this->hookSuffix = add_management_page(
            __('Low score pages', 'wp-seo-next'),
            __('Low score pages', 'wp-seo-next'),
            Capabilities::CAP_TOOLS,
            self::PAGE_SLUG,
            [$this, 'renderPage']
        );

        if (is_string($this->hookSuffix) && $this->hookSuffix !== '') {
            add_action('load-' . $this->hookSuffix, [$this, 'addScreenOptions']);
        }
    }

    public function addScreenOptions(): void
    {
        add_screen_option('per_page', [
            'label'   => __('Rows per page', 'wp-seo-next'),
            'default' => 20,
            'option'  => self::PER_PAGE_OPTION,
        ]);
    }

    /**
     * Persist custom screen option.
     *
     * @param mixed $status
     * @param string $option
     * @param int $value
     * @return mixed
     */
    public function setScreenOption($status, string $option, int $value)
    {
        if ($option === self::PER_PAGE_OPTION) {
            $v = (int) $value;
            return $v > 0 ? min(200, $v) : 20;
        }
        return $status;
    }

    public function renderPage(): void
    {
        if (!Capabilities::canManageTools()) {
            wp_die(esc_html__('You do not have permission to access this page.', 'wp-seo-next'));
        }

        $filters = $this->currentReportFilters();

        $table = new LowScoreListTable($filters);
        $table->prepare_items();

        echo '<div class="wrap">';
        echo '<h1>' . esc_html__('Low score pages', 'wp-seo-next') . '</h1>';
        echo '<p class="description">' . esc_html__('Lists pages with a low on-page SEO score (rule-based). Use it to quickly find pages that need basic fixes.', 'wp-seo-next') . '</p>';

        echo '<form method="get">';
        echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';

        // Filters row.
        echo '<div style="display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin:10px 0 12px;">';

        // Post type filter.
        $postTypes = get_post_types(['public' => true], 'objects');
        if (isset($postTypes['attachment'])) {
            unset($postTypes['attachment']);
        }

        echo '<div>';
        echo '<label for="wpsn_pt"><strong>' . esc_html__('Post type', 'wp-seo-next') . '</strong></label><br/>';
        echo '<select id="wpsn_pt" name="pt">';
        echo '<option value="any">' . esc_html__('Any', 'wp-seo-next') . '</option>';
        foreach ($postTypes as $pt => $obj) {
            $label = $obj->labels->singular_name ?? $pt;
            echo '<option value="' . esc_attr($pt) . '" ' . selected($filters['pt'], $pt, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select>';
        echo '</div>';

        // Status filter.
        $status = $filters['status'];
        $statusOptions = [
            'publish' => __('Published', 'wp-seo-next'),
            'draft' => __('Draft', 'wp-seo-next'),
            'pending' => __('Pending', 'wp-seo-next'),
            'private' => __('Private', 'wp-seo-next'),
            'future' => __('Scheduled', 'wp-seo-next'),
            'any' => __('Any', 'wp-seo-next'),
        ];
        echo '<div>';
        echo '<label for="wpsn_status"><strong>' . esc_html__('Status', 'wp-seo-next') . '</strong></label><br/>';
        echo '<select id="wpsn_status" name="status">';
        foreach ($statusOptions as $k => $lbl) {
            echo '<option value="' . esc_attr($k) . '" ' . selected($status, $k, false) . '>' . esc_html($lbl) . '</option>';
        }
        echo '</select>';
        echo '</div>';

        // Max score.
        echo '<div>';
        echo '<label for="wpsn_max"><strong>' . esc_html__('Max score', 'wp-seo-next') . '</strong></label><br/>';
        echo '<input id="wpsn_max" name="max" type="number" min="0" max="100" value="' . esc_attr((string) $filters['max']) . '" style="width:90px;" />';
        echo '</div>';

        // Include not analyzed.
        echo '<div style="padding:0 0 3px 0;">';
        echo '<label style="display:inline-flex;gap:6px;align-items:center;">';
        echo '<input type="checkbox" name="include_na" value="1" ' . checked($filters['include_na'], true, false) . ' />';
        echo '<span>' . esc_html__('Include not analyzed', 'wp-seo-next') . '</span>';
        echo '</label>';
        echo '</div>';

        // Submit.
        echo '<div>';
        submit_button(__('Filter', 'wp-seo-next'), 'secondary', '', false);
        echo '</div>';
        echo '</div>';

        // Search + table.
        $table->search_box(__('Search titles', 'wp-seo-next'), 'wpsn-low-score-search');
        $table->display();

        echo '</form>';
        echo '</div>';
    }

    /**
     * Make the SEO score column sortable on all public post types.
     */
    public function registerSortableColumns(): void
    {
        if (!is_admin()) {
            return;
        }

        $postTypes = get_post_types(['public' => true], 'names');
        foreach ($postTypes as $pt) {
            if ($pt === 'attachment') {
                continue;
            }

            add_filter('manage_edit-' . $pt . '_sortable_columns', static function (array $cols): array {
                $cols['wpsn_seo_score'] = 'wpsn_seo_score';
                return $cols;
            });
        }
    }

    /**
     * Add a score filter dropdown to the post list screen.
     */
    public function renderScoreFilter(): void
    {
        if (!is_admin()) {
            return;
        }

        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        if (!$screen || $screen->base !== 'edit') {
            return;
        }

        $pt = (string) ($screen->post_type ?? 'post');
        if ($pt === 'attachment') {
            return;
        }

        $selected = isset($_GET['wpsn_score_filter']) ? sanitize_key((string) wp_unslash($_GET['wpsn_score_filter'])) : '';
        $options = [
            '' => __('All scores', 'wp-seo-next'),
            'good' => __('Good (80–100)', 'wp-seo-next'),
            'ok' => __('OK (55–79)', 'wp-seo-next'),
            'low' => __('Low (0–54)', 'wp-seo-next'),
            'na' => __('Not analyzed', 'wp-seo-next'),
        ];

        echo '<label class="screen-reader-text" for="wpsn_score_filter">' . esc_html__('Filter by SEO score', 'wp-seo-next') . '</label>';
        echo '<select name="wpsn_score_filter" id="wpsn_score_filter" style="max-width:180px;">';
        foreach ($options as $k => $label) {
            echo '<option value="' . esc_attr($k) . '" ' . selected($selected, $k, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select>';
    }

    /**
     * Apply score filtering + flag score ordering on the post list query.
     */
    public function handlePostsListQuery(\WP_Query $query): void
    {
        if (!is_admin() || !$query->is_main_query()) {
            return;
        }

        global $pagenow;
        if ($pagenow !== 'edit.php') {
            return;
        }

        $postType = (string) ($query->get('post_type') ?: 'post');
        if ($postType === 'attachment') {
            return;
        }

        // Filter.
        $filter = isset($_GET['wpsn_score_filter']) ? sanitize_key((string) wp_unslash($_GET['wpsn_score_filter'])) : '';
        if ($filter !== '') {
            $mq = $query->get('meta_query');
            $mq = is_array($mq) ? $mq : [];

            if ($filter === 'good') {
                $mq[] = [
                    'key' => MetaKeys::ANALYSIS_SCORE,
                    'value' => 80,
                    'compare' => '>=',
                    'type' => 'NUMERIC',
                ];
            } elseif ($filter === 'ok') {
                $mq[] = [
                    'key' => MetaKeys::ANALYSIS_SCORE,
                    'value' => [55, 79],
                    'compare' => 'BETWEEN',
                    'type' => 'NUMERIC',
                ];
            } elseif ($filter === 'low') {
                $mq[] = [
                    'key' => MetaKeys::ANALYSIS_SCORE,
                    'value' => 54,
                    'compare' => '<=',
                    'type' => 'NUMERIC',
                ];
            } elseif ($filter === 'na') {
                $mq[] = [
                    'key' => MetaKeys::ANALYSIS_SCORE,
                    'compare' => 'NOT EXISTS',
                ];
            }

            if (!empty($mq)) {
                $query->set('meta_query', $mq);
            }
        }

        // Sorting.
        $orderby = (string) $query->get('orderby');
        if ($orderby === 'wpsn_seo_score') {
            // Flag for posts_clauses filter.
            $query->set('wpsn_orderby_score', 1);
        }
    }

    /**
     * Implement score ordering via LEFT JOIN so posts without the score meta
     * are still included (and show as "—").
     *
     * @param array<string,string> $clauses
     * @return array<string,string>
     */
    public function filterPostsClausesForScoreOrder(array $clauses, \WP_Query $query): array
    {
        if (!is_admin() || !$query->is_main_query()) {
            return $clauses;
        }

        global $pagenow;
        if ($pagenow !== 'edit.php') {
            return $clauses;
        }

        $flag = (int) $query->get('wpsn_orderby_score');
        $orderby = (string) $query->get('orderby');
        if ($flag !== 1 && $orderby !== 'wpsn_seo_score') {
            return $clauses;
        }

        $postType = (string) ($query->get('post_type') ?: 'post');
        if ($postType === 'attachment') {
            return $clauses;
        }

        global $wpdb;

        $alias = 'wpsn_score_meta';
        if (strpos($clauses['join'], $alias) === false) {
            $metaKey = esc_sql(MetaKeys::ANALYSIS_SCORE);
            $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS {$alias} ON ({$wpdb->posts}.ID = {$alias}.post_id AND {$alias}.meta_key = '{$metaKey}') ";
        }

        $order = strtoupper((string) $query->get('order'));
        $order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';

        // Treat missing score as -1 (first in ASC, last in DESC).
        $scoreExpr = "COALESCE(CAST({$alias}.meta_value AS SIGNED), -1)";

        // Preserve a deterministic secondary sort.
        $clauses['orderby'] = $scoreExpr . ' ' . $order . ', ' . $wpdb->posts . '.post_date DESC';

        return $clauses;
    }

    /**
     * @return array{pt:string,status:string,max:int,include_na:bool,s:string}
     */
    private function currentReportFilters(): array
    {
        $pt = isset($_GET['pt']) ? sanitize_key((string) wp_unslash($_GET['pt'])) : 'any';
        if ($pt !== 'any' && $pt !== '' && !post_type_exists($pt)) {
            $pt = 'any';
        }

        $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : 'publish';
        $allowedStatus = ['publish', 'draft', 'pending', 'private', 'future', 'any'];
        if (!in_array($status, $allowedStatus, true)) {
            $status = 'publish';
        }

        $max = isset($_GET['max']) ? (int) $_GET['max'] : 54;
        $max = max(0, min(100, $max));

        $includeNa = !empty($_GET['include_na']);

        $s = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';

        return [
            'pt' => $pt,
            'status' => $status,
            'max' => $max,
            'include_na' => (bool) $includeNa,
            's' => $s,
        ];
    }
}
