<?php

namespace WP_STATISTICS;

if (!defined('ABSPATH')) exit; // Exit if accessed directly

use WP_Statistics\Components\DateRange;
use WP_Statistics\Models\VisitorsModel;
use WP_Statistics\Service\Geolocation\GeolocationFactory;
use WP_Statistics\Service\Geolocation\Provider\DbIpProvider;
use WP_Statistics\Service\Geolocation\Provider\MaxmindGeoIPProvider;
use WP_Statistics\Utils\Request;

/**
 * @deprecated Use WP_Statistics\AjaxHandler\AjaxManager instead for global AJAX callbacks.
 *
 * @todo Refactor this class and move ajax action to their services or global AjaxManager file
 */
class Ajax
{
    public function __construct()
    {
        add_action('init', [$this, 'registerAjaxCallbacks']);
    }

    /**
     * Register AJAX callbacks
     */
    public function registerAjaxCallbacks()
    {
        /**
         * List Of Setup Ajax request in WordPress
         */
        $list = [
            [
                'class'  => $this,
                'action' => 'update_geoip_database',
                'public' => false
            ],
            [
                'class'  => $this,
                'action' => 'admin_meta_box',
                'public' => false
            ],
            [
                'class'  => $this,
                'action' => 'get_page_filter_items',
                'public' => false
            ],
            [
                'class'  => $this,
                'action' => 'search_visitors',
                'public' => false
            ],
            [
                'class'  => $this,
                'action' => 'store_date_range',
                'public' => false
            ],
            [
                'class'  => $this,
                'action' => 'dismiss_notices',
                'public' => false
            ]
        ];

        /**
         * @deprecated: wp_statistics_ajax_list hook was deprecated since v14.16.
         * Use AjaxManager class instead to define global AJAX callbacks.
        */
        $list = apply_filters('wp_statistics_ajax_list', $list);

        foreach ($list as $item) {
            $class    = $item['class'];
            $action   = $item['action'];
            $callback = $action . '_action_callback';
            $isPublic = isset($item['public']) && $item['public'] == true ? true : false;

            // If callback exists in the class, register the action
            if (!empty($class) && method_exists($class, $callback)) {
                add_action('wp_ajax_wp_statistics_' . $action, [$class, $callback]);

                // Register the AJAX callback publicly
                if ($isPublic) {
                    add_action('wp_ajax_nopriv_wp_statistics_' . $action, [$class, $callback]);
                }
            }
        }
    }

    /**
     * Checks if the current request is allowed to perform this action.
     *
     * This method ensures that:
     * 1. The request is an AJAX request.
     * 2. The user has the required access permission (passed as $requiredPermission).
     * 3. The AJAX nonce is valid.
     *
     * If any of these checks fail, it immediately returns a JSON error response
     * and exits the script.
     *
     * @param string $requiredPermission The capability required to access this action (default: 'manage').
     * @param string $nonceAction Optional. The nonce action to verify (default: 'wp_rest').
     * @param string $nonceName Optional. The nonce field name (default: 'wps_nonce').
     *
     * @return void
     */
    private function checkAccess($requiredPermission = 'manage', $nonceAction = 'wp_rest', $nonceName = 'wps_nonce')
    {
        if (!Request::isFrom('ajax') || !User::Access($requiredPermission)) {
            wp_send_json_error([
                'message' => esc_html__('Unauthorized.', 'wp-statistics')
            ]);
        }

        $nonceValid = check_ajax_referer($nonceAction, $nonceName, false);
        if (!$nonceValid) {
            wp_send_json_error([
                'message' => esc_html__('Invalid nonce.', 'wp-statistics')
            ]);
        }
    }


    /**
     * Setup an AJAX action to update geoIP database.
     */
    public function update_geoip_database_action_callback()
    {
        $this->checkAccess('manage');

        $method   = Request::get('geoip_location_detection_method', 'maxmind');
        $provider = MaxmindGeoIPProvider::class;

        if ('dbip' === $method) {
            $provider = DbIpProvider::class;
        }

        $result = GeolocationFactory::downloadDatabase($provider);

        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        wp_send_json_success(['message' => esc_html__('GeoIP Database successfully updated.', 'wp-statistics')]);
    }

    /**
     * Setup Admin Meta box render ajax
     */
    public function admin_meta_box_action_callback()
    {
        $this->checkAccess('read');

        $metaboxName = sanitize_text_field($_GET['name']);

        // Check Exist MetaBox Name
        if (in_array($metaboxName, array_keys(Meta_Box::getList())) and Meta_Box::metaBoxClassExist($metaboxName)) {

            $parameters = [];
            foreach ($_GET as $key => $value) {
                if ($value && !in_array($key, ['action', 'wps_nonce', '_'])) {
                    $parameters[$key] = sanitize_text_field($value);
                }
            }

            $class = Meta_Box::getMetaBoxClass($metaboxName);

            wp_send_json($class::get($parameters));

        } else {
            wp_send_json(array('code' => 'not_found_meta_box', 'message' => __('Invalid MetaBox Name in Request.', 'wp-statistics')), 400);
        }

        exit;
    }

    /**
     * Get page filter items
     */
    public function get_page_filter_items_action_callback()
    {
        $this->checkAccess('read');

        $paged         = Request::get('paged', 1, 'number');
        $postType      = Request::get('post_type', array_values(Helper::get_list_post_type()));
        $authorId      = Request::get('author_id', '', 'number');
        $search        = Request::get('search', '');
        $page          = Request::get('page');
        $selectedPost  = Request::get('post_id', false, 'number');
        $hideAllOption = Request::get('hide_all_option', false);

        if (!$page) {
            wp_send_json([
                'code'    => 'not_found_page',
                'message' => esc_html__('Invalid Page in Request.', 'wp-statistics')
            ], 400);
        }

        $query = new \WP_Query([
            'post_status'    => 'publish',
            'posts_per_page' => 10,
            'paged'          => $paged,
            'post_type'      => $postType,
            'author'         => $authorId,
            's'              => $search
        ]);

        $posts = [];
        if ($query->have_posts()) {
            if (empty($hideAllOption) && $paged == 1 && empty($search)) {
                $allOption = [
                    'id'   => Menus::admin_url($page),
                    'text' => esc_html__('All', 'wp-statistics')
                ];

                if (!$selectedPost) {
                    $allOption['selected'] = true;
                }

                $posts[] = $allOption;
            }

            while ($query->have_posts()) {
                $query->the_post();

                $option = [
                    'id'   => get_the_ID(),
                    'text' => get_the_title()
                ];

                if ($selectedPost == get_the_ID()) {
                    $option['selected'] = true;
                }

                $posts[] = $option;
            }
        }

        wp_send_json([
            'results'    => $posts,
            'pagination' => [
                'more' => $query->max_num_pages > $paged ? true : false
            ]
        ]);
    }

    public function search_visitors_action_callback()
    {
        $this->checkAccess('read');

        $results = [];
        $search  = Request::get('search', '');

        $visitorsModel = new VisitorsModel();
        $visitors      = $visitorsModel->searchVisitors([
            'ip'       => $search,
            'username' => $search,
            'email'    => $search
        ]);

        foreach ($visitors as $visitor) {
            $option = [
                'id'   => Menus::admin_url('visitors', ['type' => 'single-visitor', 'visitor_id' => $visitor->ID]),
                /* translators: %s: string value */
                'text' => sprintf(esc_html__('Visitor (#%s)', 'wp-statistics'), $visitor->ID)
            ];

            $results[] = $option;
        }

        wp_send_json(['results' => $results]);
    }

    public function store_date_range_action_callback()
    {
        $this->checkAccess('read');

        $date = Request::get('date', [], 'array');
        DateRange::store($date);

        wp_send_json_success(['message' => esc_html__('Date range has been stored successfully.', 'wp-statistics')]);
    }

    public function dismiss_notices_action_callback()
    {
        $this->checkAccess('manage');

        $noticeId = Request::get('notice_id');

        if (!empty($noticeId)) {
            $dismissedNotices = get_option('wp_statistics_dismissed_notices', []);

            if (!in_array($noticeId, $dismissedNotices)) {
                $dismissedNotices[] = $noticeId;
                update_option('wp_statistics_dismissed_notices', $dismissedNotices);
            }

            wp_send_json_success(['message' => esc_html__('Notice dismissed.', 'wp-statistics')]);
        } else {
            wp_send_json_error(['message' => esc_html__('Invalid Notice ID.', 'wp-statistics')]);
        }

        exit;
    }
}

new Ajax;
