<?php
declare(strict_types=1);

namespace ProRank\SEO\Core\RestApi;

use WP_Error;
use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Post index status endpoint.
 */
class IndexStatusController extends WP_REST_Controller {

    protected $namespace = 'prorank-seo/v1';
    protected $rest_base = 'index-status';

    public function register_routes(): void {
        register_rest_route($this->namespace, '/' . $this->rest_base . '/post/(?P<post_id>\d+)', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_post_status'],
                'permission_callback' => [$this, 'can_read_post'],
            ],
        ]);

        register_rest_route($this->namespace, '/' . $this->rest_base . '/bulk', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_bulk_status'],
                'permission_callback' => [$this, 'can_read_items'],
                'args' => [
                    'post_ids' => [
                        'required' => false,
                        'type' => 'array',
                        'items' => ['type' => 'integer'],
                    ],
                    'limit' => [
                        'required' => false,
                        'type' => 'integer',
                        'default' => 20,
                    ],
                ],
            ],
        ]);
    }

    public function can_read_post(WP_REST_Request $request): bool {
        $post_id = absint($request->get_param('post_id'));
        return $post_id > 0 && current_user_can('edit_post', $post_id);
    }

    public function can_read_items(): bool {
        return current_user_can('edit_posts');
    }

    public function get_post_status(WP_REST_Request $request) {
        $post_id = absint($request->get_param('post_id'));
        $post = get_post($post_id);
        if (!$post) {
            return new WP_Error('post_not_found', __('Post not found.', 'prorank-seo'), ['status' => 404]);
        }

        return new WP_REST_Response([
            'success' => true,
            'data' => $this->build_status($post),
        ]);
    }

    public function get_bulk_status(WP_REST_Request $request): WP_REST_Response {
        $ids = $request->get_param('post_ids');
        if (!is_array($ids) || empty($ids)) {
            $limit = max(1, min(100, absint($request->get_param('limit')) ?: 20));
            $ids = get_posts([
                'post_type' => get_post_types(['public' => true]),
                'post_status' => ['publish', 'draft', 'pending', 'future'],
                'fields' => 'ids',
                'posts_per_page' => $limit,
                'orderby' => 'modified',
                'order' => 'DESC',
            ]);
        }

        $items = [];
        foreach (array_slice(array_map('absint', $ids), 0, 100) as $post_id) {
            $post = get_post($post_id);
            if ($post && current_user_can('edit_post', $post_id)) {
                $items[] = $this->build_status($post);
            }
        }

        return new WP_REST_Response([
            'success' => true,
            'data' => $items,
        ]);
    }

    private function build_status(\WP_Post $post): array {
        $post_id = (int) $post->ID;
        $permalink = get_permalink($post);
        $robots = get_post_meta($post_id, '_prorank_meta_robots', true);
        $canonical = (string) get_post_meta($post_id, '_prorank_seo_canonical_url', true);
        $noindex = $this->has_noindex($robots) || $this->has_noindex_meta($post_id);
        $reasons = [];

        if ((int) get_option('blog_public', 1) !== 1) {
            $reasons[] = 'site_discourages_search_engines';
        }
        if ($post->post_status !== 'publish') {
            $reasons[] = 'post_not_published';
        }
        if ($noindex) {
            $reasons[] = 'noindex_directive';
        }
        if ($canonical !== '' && is_string($permalink) && untrailingslashit($canonical) !== untrailingslashit($permalink)) {
            $reasons[] = 'canonical_points_elsewhere';
        }

        $local_status = empty($reasons) ? 'indexable' : 'not_indexable';
        $google = $this->get_google_index_status(is_string($permalink) ? $permalink : '');

        return [
            'post_id' => $post_id,
            'post_type' => $post->post_type,
            'post_status' => $post->post_status,
            'title' => get_the_title($post_id),
            'url' => is_string($permalink) ? $permalink : '',
            'local_status' => $local_status,
            'google_status' => $google['status'],
            'google' => $google,
            'noindex' => $noindex,
            'canonical_url' => $canonical,
            'reasons' => $reasons,
            'checked_at' => current_time('mysql'),
        ];
    }

    private function has_noindex($robots): bool {
        if (is_array($robots)) {
            return in_array('noindex', array_map('strval', $robots), true) || !empty($robots['noindex']);
        }

        if (is_string($robots)) {
            return stripos($robots, 'noindex') !== false;
        }

        return false;
    }

    private function has_noindex_meta(int $post_id): bool {
        foreach (['_prorank_seo_noindex', '_prorank_seo_robots_noindex', '_prorank_robots_noindex'] as $key) {
            $value = get_post_meta($post_id, $key, true);
            if (is_bool($value) && $value) {
                return true;
            }
            if (is_numeric($value) && (int) $value === 1) {
                return true;
            }
            if (is_string($value) && in_array(strtolower(trim($value)), ['1', 'on', 'yes', 'true', 'noindex'], true)) {
                return true;
            }
        }

        return false;
    }

    private function get_google_index_status(string $url): array {
        if ($url === '') {
            return ['status' => 'unknown', 'source' => 'none'];
        }

        if (!class_exists('\ProRank\SEO\Core\Google\GSCProxyClient')) {
            return ['status' => 'unknown', 'source' => 'local_only'];
        }

        try {
            $client = new \ProRank\SEO\Core\Google\GSCProxyClient();
            if (!method_exists($client, 'is_connected') || !$client->is_connected()) {
                return ['status' => 'unknown', 'source' => 'gsc_not_connected'];
            }

            $site_url = (string) get_option('prorank_gsc_selected_site', '');
            if ($site_url === '') {
                return ['status' => 'unknown', 'source' => 'gsc_no_property'];
            }

            if (method_exists($client, 'inspect_url')) {
                $inspection = $client->inspect_url($url, $site_url);
            } else {
                $inspection = $client->api_request('/urlInspection/index:inspect', [
                    'inspectionUrl' => $url,
                    'siteUrl' => $site_url,
                ]);
            }

            if (is_wp_error($inspection)) {
                return ['status' => 'unknown', 'source' => 'gsc_error', 'message' => $inspection->get_error_message()];
            }

            if (!is_array($inspection)) {
                return ['status' => 'unknown', 'source' => 'gsc_unexpected_response'];
            }

            $coverage = $inspection['inspectionResult']['indexStatusResult']['coverageState']
                ?? $inspection['indexStatusResult']['coverageState']
                ?? '';
            $verdict = $inspection['inspectionResult']['indexStatusResult']['verdict']
                ?? $inspection['indexStatusResult']['verdict']
                ?? '';

            $coverage_text = strtolower((string) $coverage);
            $verdict_text = strtoupper((string) $verdict);
            $status = 'unknown';
            if ($verdict_text === 'PASS') {
                $status = 'indexed';
            } elseif (
                str_contains($coverage_text, 'not indexed')
                || str_contains($coverage_text, 'blocked')
                || str_contains($coverage_text, 'excluded')
                || $verdict_text === 'FAIL'
            ) {
                $status = 'not_indexed';
            } elseif (str_contains($coverage_text, 'indexed') || str_contains($coverage_text, 'url is on google')) {
                $status = 'indexed';
            }

            return [
                'status' => $status,
                'source' => 'gsc_url_inspection',
                'coverage_state' => (string) $coverage,
                'verdict' => (string) $verdict,
                'raw' => $inspection,
            ];
        } catch (\Throwable $e) {
            return ['status' => 'unknown', 'source' => 'gsc_exception', 'message' => $e->getMessage()];
        }
    }
}
