<?php
/**
 * News AJAX handler for Noesia Smart Waiter.
 *
 * Self-contained module-local AJAX class.
 * Returns raw RSS XML so the existing News JS can continue parsing client-side.
 *
 * Fixes:
 * - applies the saved dashboard language when the request does not pass one
 * - respects panel/topic/query so each News card can receive its own feed
 * - keeps the legacy AJAX action name for backward compatibility
 *
 * @package Noesia_Smart_Waiter
 */

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

if ( ! class_exists( 'Noesia_SWA_Module_News_Ajax' ) ) {
    /**
     * Module-local News AJAX.
     */
    class Noesia_SWA_Module_News_Ajax {

        /**
         * Register AJAX hooks.
         */
        public function __construct() {
            add_action( 'wp_ajax_noesia_swa_fetch_news', array( $this, 'handle_fetch_news' ) );
        }

        /**
         * Fetch Google News RSS for the selected language/country/topic.
         *
         * Keeps the same action name and XML response type expected by the
         * current module-local JS.
         *
         * @return void
         */
        public function handle_fetch_news() {
            check_ajax_referer( 'noesia_swa_nonce', 'nonce' );

            if ( ! current_user_can( 'manage_options' ) ) {
                wp_die( esc_html__( 'Unauthorized', 'noesia-smart-waiter' ), '', array( 'response' => 403 ) );
            }

            $options = get_option( 'noesia_swa_options', array() );
            $options = is_array( $options ) ? $options : array();

            $lang    = $this->resolve_language( $options );
            $country = $this->resolve_country( $lang, $options );
            $panel   = $this->resolve_panel();
            $topic   = $this->resolve_topic();
            $query   = $this->resolve_query( $panel, $lang );
            $rss_url = $this->build_rss_url( $lang, $country, $topic, $query );

            $response = wp_remote_get(
                $rss_url,
                array(
                    'timeout'    => 15,
                    'user-agent' => 'Noesia Smart Waiter/' . ( defined( 'NOESIA_SWA_VERSION' ) ? NOESIA_SWA_VERSION : '1.0.0' ) . '; ' . home_url( '/' ),
                    'headers'    => array(
                        'Accept' => 'application/rss+xml, application/xml, text/xml;q=0.9, */*;q=0.8',
                    ),
                )
            );

            if ( is_wp_error( $response ) ) {
                wp_die(
                    esc_html( $response->get_error_message() ),
                    '',
                    array(
                        'response' => 500,
                    )
                );
            }

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

            if ( 200 !== $code || '' === trim( $body ) ) {
                wp_die(
                    esc_html__( 'Unable to fetch news feed.', 'noesia-smart-waiter' ),
                    '',
                    array(
                        'response' => 500,
                    )
                );
            }

            nocache_headers();
            header( 'Content-Type: application/xml; charset=' . get_option( 'blog_charset' ) );
            echo $body; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            exit;
        }

        /**
         * Resolve the request language with saved-settings fallback.
         *
         * @param array<string,mixed> $options Plugin options.
         * @return string
         */
        private function resolve_language( $options ) {
            $allowed = array( 'en', 'el', 'de', 'fr', 'it', 'es' );
            $lang    = $this->post_key( 'lang' );

            if ( '' === $lang && isset( $options['news_language'] ) ) {
                $lang = sanitize_key( (string) $options['news_language'] );
            }

            if ( '' === $lang && isset( $options['weather_language'] ) ) {
                $lang = sanitize_key( (string) $options['weather_language'] );
            }

            if ( ! in_array( $lang, $allowed, true ) ) {
                $lang = 'en';
            }

            return $lang;
        }

        /**
         * Resolve the News country code from the current request or settings.
         *
         * @param string              $lang    Language code.
         * @param array<string,mixed> $options Plugin options.
         * @return string
         */
        private function resolve_country( $lang, $options ) {
            $country = strtoupper( preg_replace( '/[^A-Z]/', '', $this->post_text( 'country' ) ) );
            $country = substr( (string) $country, 0, 2 );

            if ( '' !== $country ) {
                return $country;
            }

            $service = $this->get_news_service();

            if ( is_object( $service ) && method_exists( $service, 'get_news_country' ) ) {
                return (string) $service->get_news_country( $lang, $options );
            }

            if ( is_array( $options ) ) {
                if ( ! empty( $options['news_country'] ) ) {
                    $country = strtoupper( preg_replace( '/[^A-Z]/', '', sanitize_text_field( (string) $options['news_country'] ) ) );
                    $country = substr( (string) $country, 0, 2 );

                    if ( '' !== $country ) {
                        return $country;
                    }
                }

                if ( ! empty( $options['weather_country'] ) ) {
                    $country = strtoupper( preg_replace( '/[^A-Z]/', '', sanitize_text_field( (string) $options['weather_country'] ) ) );
                    $country = substr( (string) $country, 0, 2 );

                    if ( '' !== $country ) {
                        return $country;
                    }
                }
            }

            return $this->map_country_from_lang( $lang );
        }

        /**
         * Resolve the module-owned News service through the shared service contract.
         *
         * @return object|null
         */
        private function get_news_service() {
            $service_key   = 'news';
            $service_class = apply_filters( 'noesia_swa_service_class', 'Noesia_SWA_News_Service', $service_key, $this );
            $service_class = is_string( $service_class ) ? $service_class : 'Noesia_SWA_News_Service';

            if ( ! class_exists( $service_class ) ) {
                $service_file = apply_filters( 'noesia_swa_service_file', '', $service_key, $service_class, $this );

                if ( ! is_string( $service_file ) || '' === $service_file ) {
                    $service_file = NOESIA_SWA_PLUGIN_DIR . 'modules/free/news/includes/class-noesia-swa-news-service.php';
                }

                if ( file_exists( $service_file ) ) {
                    require_once $service_file;
                }
            }

            if ( class_exists( $service_class ) ) {
                return new $service_class();
            }

            return null;
        }

        /**
         * Resolve the current News panel key.
         *
         * @return string
         */
        private function resolve_panel() {
            $panel = $this->post_key( 'panel' );

            return in_array( $panel, array( 'general', 'business', 'health', 'economy', 'politics', 'world' ), true )
                ? $panel
                : '';
        }

        /**
         * Resolve a supported Google News topic token.
         *
         * @return string
         */
        private function resolve_topic() {
            $topic = strtoupper( $this->post_key( 'topic' ) );

            return in_array( $topic, array( 'WORLD', 'NATION', 'BUSINESS', 'TECHNOLOGY', 'ENTERTAINMENT', 'SCIENCE', 'SPORTS', 'HEALTH' ), true )
                ? $topic
                : '';
        }

        /**
         * Resolve a search query for panels that should not share a topic feed.
         *
         * @param string $panel Panel key.
         * @param string $lang  Language code.
         * @return string
         */
        private function resolve_query( $panel, $lang ) {
            $query = $this->post_text( 'query' );
            $query = trim( preg_replace( '/\s+/', ' ', $query ) );

            if ( '' !== $query ) {
                return $query;
            }

            if ( 'economy' === $panel ) {
                $map = array(
                    'en' => 'economy OR inflation OR markets OR finance',
                    'el' => 'οικονομία OR πληθωρισμός OR αγορές OR χρηματοδότηση',
                    'de' => 'wirtschaft OR inflation OR märkte OR finanzen',
                    'fr' => 'économie OR inflation OR marchés OR finance',
                    'it' => 'economia OR inflazione OR mercati OR finanza',
                    'es' => 'economía OR inflación OR mercados OR finanzas',
                );

                return isset( $map[ $lang ] ) ? $map[ $lang ] : $map['en'];
            }

            if ( 'politics' === $panel ) {
                $map = array(
                    'en' => 'politics OR government OR parliament',
                    'el' => 'πολιτική OR κυβέρνηση OR βουλή',
                    'de' => 'politik OR regierung OR parlament',
                    'fr' => 'politique OR gouvernement OR parlement',
                    'it' => 'politica OR governo OR parlamento',
                    'es' => 'política OR gobierno OR parlamento',
                );

                return isset( $map[ $lang ] ) ? $map[ $lang ] : $map['en'];
            }

            return '';
        }


        /**
         * Read a POST scalar key after the request nonce has been verified.
         *
         * @param string $key Request key.
         * @return string
         */
        private function post_key( $key ) {
            // phpcs:disable WordPress.Security.NonceVerification.Missing -- handle_fetch_news() verifies the request nonce before these helpers are used.
            if ( ! isset( $_POST[ $key ] ) ) {
                return '';
            }

            return sanitize_key( wp_unslash( (string) $_POST[ $key ] ) );
            // phpcs:enable WordPress.Security.NonceVerification.Missing
        }

        /**
         * Read a POST text field after the request nonce has been verified.
         *
         * @param string $key Request key.
         * @return string
         */
        private function post_text( $key ) {
            // phpcs:disable WordPress.Security.NonceVerification.Missing -- handle_fetch_news() verifies the request nonce before these helpers are used.
            if ( ! isset( $_POST[ $key ] ) ) {
                return '';
            }

            return sanitize_text_field( wp_unslash( (string) $_POST[ $key ] ) );
            // phpcs:enable WordPress.Security.NonceVerification.Missing
        }

        /**
         * Build the final Google News RSS endpoint.
         *
         * @param string $lang    Language code.
         * @param string $country Country code.
         * @param string $topic   Google topic token.
         * @param string $query   Query string.
         * @return string
         */
        private function build_rss_url( $lang, $country, $topic, $query ) {
            $lang_for_google = ( 'en' === $lang ) ? 'en-US' : $lang;
            $args            = array(
                'hl'   => $lang_for_google,
                'gl'   => $country,
                'ceid' => $country . ':' . $lang,
            );

            if ( '' !== $topic ) {
                return add_query_arg(
                    $args,
                    'https://news.google.com/rss/headlines/section/topic/' . rawurlencode( $topic )
                );
            }

            if ( '' !== $query ) {
                $args['q'] = $query;
                return add_query_arg( $args, 'https://news.google.com/rss/search' );
            }

            return add_query_arg( $args, 'https://news.google.com/rss' );
        }

        /**
         * Map language code to Google News country code.
         *
         * @param string $lang Language code.
         * @return string
         */
        private function map_country_from_lang( $lang ) {
            $map = array(
                'en' => 'US',
                'el' => 'GR',
                'de' => 'DE',
                'fr' => 'FR',
                'it' => 'IT',
                'es' => 'ES',
            );

            return isset( $map[ $lang ] ) ? $map[ $lang ] : 'US';
        }
    }
}
