<?php
/**
 * Google News RSS — circuit breaker, backoff, stale fallback (mirrors Trends module).
 *
 * @package Newsroom_AI_Assistant
 */

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

if ( ! defined( 'NEWSAI_NEWS_FAIL_THRESHOLD' ) ) {
	define( 'NEWSAI_NEWS_FAIL_THRESHOLD', 3 );
}

if ( ! defined( 'NEWSAI_NEWS_CIRCUIT_BASE' ) ) {
	define( 'NEWSAI_NEWS_CIRCUIT_BASE', 15 * MINUTE_IN_SECONDS );
}

if ( ! defined( 'NEWSAI_NEWS_CIRCUIT_MAX' ) ) {
	define( 'NEWSAI_NEWS_CIRCUIT_MAX', HOUR_IN_SECONDS );
}

if ( ! defined( 'NEWSAI_NEWS_NEGATIVE_CACHE_TTL' ) ) {
	define( 'NEWSAI_NEWS_NEGATIVE_CACHE_TTL', 2 * MINUTE_IN_SECONDS );
}

if ( ! defined( 'NEWSAI_NEWS_FAIL_WINDOW' ) ) {
	define( 'NEWSAI_NEWS_FAIL_WINDOW', 10 * MINUTE_IN_SECONDS );
}

if ( ! defined( 'NEWSAI_NEWS_STALE_TTL' ) ) {
	define( 'NEWSAI_NEWS_STALE_TTL', DAY_IN_SECONDS );
}

if ( ! function_exists( 'newsai_google_news_normalize_beat' ) ) {
	/**
	 * @param string $beat Raw beat slug.
	 * @return string One of s,b,e,m,t or empty.
	 */
	function newsai_google_news_normalize_beat( $beat ) {
		$beat = sanitize_key( (string) $beat );
		return in_array( $beat, [ 's', 'b', 'e', 'm', 't' ], true ) ? $beat : '';
	}
}

if ( ! function_exists( 'newsai_google_news_feed_identity' ) ) {
	/**
	 * Stable key per edition + beat (circuit / stale scope).
	 *
	 * @param string $geo  Country code.
	 * @param string $beat Optional beat.
	 * @return string
	 */
	function newsai_google_news_feed_identity( $geo, $beat = '' ) {
		if ( ! function_exists( 'newsai_news_edition_for_geo' ) ) {
			$geo = function_exists( 'newsai_google_trends_normalize_geo' )
				? newsai_google_trends_normalize_geo( $geo )
				: strtoupper( preg_replace( '/[^A-Z]/i', '', (string) $geo ) );
			return $geo . '_' . ( newsai_google_news_normalize_beat( $beat ) !== '' ? newsai_google_news_normalize_beat( $beat ) : 'general' );
		}
		$edition = newsai_news_edition_for_geo( $geo );
		$beat    = newsai_google_news_normalize_beat( $beat );
		return strtoupper( (string) $edition['gl'] ) . '_'
			. ( $beat !== '' ? $beat : 'general' ) . '_'
			. sanitize_key( (string) $edition['ceid_tail'] );
	}
}

if ( ! function_exists( 'newsai_google_news_fail_key' ) ) {
	function newsai_google_news_fail_key( $identity ) {
		return 'newsai_news_fail_' . $identity;
	}
}

if ( ! function_exists( 'newsai_google_news_circuit_key' ) ) {
	function newsai_google_news_circuit_key( $identity ) {
		return 'newsai_news_circuit_' . $identity;
	}
}

if ( ! function_exists( 'newsai_google_news_negative_key' ) ) {
	function newsai_google_news_negative_key( $identity ) {
		return 'newsai_news_neg_' . $identity;
	}
}

if ( ! function_exists( 'newsai_google_news_stale_items_key' ) ) {
	function newsai_google_news_stale_items_key( $identity ) {
		return 'newsai_news_items_stale_' . $identity;
	}
}

if ( ! function_exists( 'newsai_google_news_stale_html_key' ) ) {
	function newsai_google_news_stale_html_key( $identity ) {
		return 'nros_news_html_stale_v2_' . $identity;
	}
}

if ( ! function_exists( 'newsai_google_news_is_circuit_open' ) ) {
	function newsai_google_news_is_circuit_open( $identity ) {
		return (bool) get_transient( newsai_google_news_circuit_key( $identity ) );
	}
}

if ( ! function_exists( 'newsai_google_news_circuit_backoff_seconds' ) ) {
	function newsai_google_news_circuit_backoff_seconds( $streak ) {
		$streak = max( 0, (int) $streak );
		if ( $streak < NEWSAI_NEWS_FAIL_THRESHOLD ) {
			return 0;
		}
		$exp = min( 4, $streak - NEWSAI_NEWS_FAIL_THRESHOLD );
		$sec = (int) ( NEWSAI_NEWS_CIRCUIT_BASE * pow( 2, $exp ) );
		return min( NEWSAI_NEWS_CIRCUIT_MAX, $sec );
	}
}

if ( ! function_exists( 'newsai_google_news_log_failure_once' ) ) {
	function newsai_google_news_log_failure_once( $identity, $reason, $streak = 0 ) {
		if ( ! apply_filters( 'newsai_google_news_log_failures', ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) ) {
			return;
		}
		$log_key = 'newsai_news_logged_' . $identity;
		if ( get_transient( $log_key ) ) {
			return;
		}
		$ttl = newsai_google_news_circuit_backoff_seconds( $streak );
		if ( $ttl < 1 ) {
			$ttl = NEWSAI_NEWS_NEGATIVE_CACHE_TTL;
		}
		set_transient( $log_key, '1', $ttl );
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
		error_log(
			sprintf(
				'[Newsroom OS] Google News RSS unavailable (%s, feed=%s, streak=%d). Circuit/backoff active.',
				$reason,
				$identity,
				(int) $streak
			)
		);
	}
}

if ( ! function_exists( 'newsai_google_news_record_success' ) ) {
	function newsai_google_news_record_success( $identity ) {
		delete_transient( newsai_google_news_fail_key( $identity ) );
		delete_transient( newsai_google_news_circuit_key( $identity ) );
		delete_transient( newsai_google_news_negative_key( $identity ) );
		delete_transient( 'newsai_news_logged_' . $identity );
	}
}

if ( ! function_exists( 'newsai_google_news_record_failure' ) ) {
	function newsai_google_news_record_failure( $identity, $reason = 'fetch_failed' ) {
		$key    = newsai_google_news_fail_key( $identity );
		$streak = (int) get_transient( $key ) + 1;
		set_transient( $key, $streak, NEWSAI_NEWS_FAIL_WINDOW );
		set_transient( newsai_google_news_negative_key( $identity ), '1', NEWSAI_NEWS_NEGATIVE_CACHE_TTL );

		$backoff = newsai_google_news_circuit_backoff_seconds( $streak );
		if ( $backoff > 0 ) {
			set_transient( newsai_google_news_circuit_key( $identity ), '1', $backoff );
			newsai_google_news_log_failure_once( $identity, $reason, $streak );
		}

		return $streak;
	}
}

if ( ! function_exists( 'newsai_google_news_get_stale_items' ) ) {
	function newsai_google_news_get_stale_items( $identity ) {
		$cached = get_transient( newsai_google_news_stale_items_key( $identity ) );
		return is_array( $cached ) ? $cached : [];
	}
}

if ( ! function_exists( 'newsai_google_news_store_stale_items' ) ) {
	function newsai_google_news_store_stale_items( $identity, array $items ) {
		if ( $items === [] ) {
			return;
		}
		set_transient( newsai_google_news_stale_items_key( $identity ), $items, NEWSAI_NEWS_STALE_TTL );
	}
}

if ( ! function_exists( 'newsai_google_news_build_urls' ) ) {
	/**
	 * Primary topic/headlines URL and optional search fallback URL.
	 *
	 * @param array  $edition From newsai_news_edition_for_geo().
	 * @param string $beat    Normalized beat or empty.
	 * @return array{primary:string,fallback:string}
	 */
	function newsai_google_news_build_urls( array $edition, $beat ) {
		$hl         = (string) $edition['hl'];
		$gl         = (string) $edition['gl'];
		$ceid_param = rawurlencode( $gl . ':' . (string) $edition['ceid_tail'] );

		$primary = 'https://news.google.com/rss?hl=' . rawurlencode( $hl )
			. '&gl=' . rawurlencode( $gl ) . '&ceid=' . $ceid_param;

		$fallback = '';
		if ( $beat !== '' ) {
			$beat_topics = [
				's' => 'SPORTS',
				'b' => 'BUSINESS',
				'e' => 'ENTERTAINMENT',
				'm' => 'HEALTH',
				't' => 'TECHNOLOGY',
			];
			$topic = isset( $beat_topics[ $beat ] ) ? $beat_topics[ $beat ] : '';
			if ( $topic !== '' ) {
				$primary = 'https://news.google.com/rss/headlines/section/topic/' . rawurlencode( $topic )
					. '?hl=' . rawurlencode( $hl ) . '&gl=' . rawurlencode( $gl ) . '&ceid=' . $ceid_param;
			}
			if ( function_exists( 'newsai_news_beat_search_query' ) ) {
				$q = newsai_news_beat_search_query( $beat, $edition );
				if ( $q !== '' ) {
					$fallback = 'https://news.google.com/rss/search?q=' . rawurlencode( $q )
						. '&hl=' . rawurlencode( $hl ) . '&gl=' . rawurlencode( $gl ) . '&ceid=' . $ceid_param;
				}
			}
		}

		return [
			'primary'  => $primary,
			'fallback' => $fallback,
		];
	}
}

if ( ! function_exists( 'newsai_google_news_fetch_feed_url' ) ) {
	/**
	 * @param string $url Feed URL.
	 * @return \WP_Error|object
	 */
	function newsai_google_news_fetch_feed_url( $url ) {
		add_filter( 'wp_feed_cache_transient_lifetime', '__return_false' );
		$nros_timeout = static function () {
			return 3;
		};
		$nros_news_http_ua = static function ( $args, $request_url = '' ) {
			if ( ! is_array( $args ) || strpos( (string) $request_url, 'news.google.com' ) === false ) {
				return $args;
			}
			$args['user-agent'] = 'Mozilla/5.0 (compatible; NewsroomOS/1.0; +' . esc_url( home_url( '/' ) ) . ') WordPress/' . get_bloginfo( 'version' );
			return $args;
		};
		add_filter( 'http_request_timeout', $nros_timeout, 999 );
		add_filter( 'http_request_args', $nros_news_http_ua, 10, 2 );
		try {
			return fetch_feed( $url );
		} finally {
			remove_filter( 'http_request_args', $nros_news_http_ua, 10 );
			remove_filter( 'http_request_timeout', $nros_timeout, 999 );
		}
	}
}

if ( ! function_exists( 'newsai_google_news_feed_has_items' ) ) {
	function newsai_google_news_feed_has_items( $feed, $limit = 1 ) {
		if ( is_wp_error( $feed ) ) {
			return false;
		}
		$items = $feed->get_items( 0, $limit );
		return is_array( $items ) && count( $items ) > 0;
	}
}

if ( ! function_exists( 'newsai_google_news_fetch_rss' ) ) {
	/**
	 * Try primary URL, then search fallback when beat is set.
	 *
	 * @param array  $edition Edition array.
	 * @param string $beat    Normalized beat.
	 * @return \WP_Error|object
	 */
	function newsai_google_news_fetch_rss( array $edition, $beat ) {
		$urls = newsai_google_news_build_urls( $edition, $beat );
		$rss  = newsai_google_news_fetch_feed_url( $urls['primary'] );

		if ( $beat !== '' && $urls['fallback'] !== '' ) {
			$need_fallback = is_wp_error( $rss );
			if ( ! $need_fallback ) {
				$need_fallback = ! newsai_google_news_feed_has_items( $rss, 8 );
			}
			if ( $need_fallback ) {
				$rss = newsai_google_news_fetch_feed_url( $urls['fallback'] );
			}
		}

		return $rss;
	}
}

if ( ! function_exists( 'newsai_google_news_parse_feed_items' ) ) {
	/**
	 * @param object $feed    SimplePie feed.
	 * @param int    $limit   Max items.
	 * @return list<array{title:string,permalink:string,time_label:string,diff_mins:int,is_fresh:bool}>
	 */
	function newsai_google_news_parse_feed_items( $feed, $limit = 8 ) {
		$items = $feed->get_items( 0, $limit );
		if ( ! is_array( $items ) ) {
			return [];
		}
		$now = (int) current_time( 'timestamp' );
		$out = [];
		foreach ( $items as $item ) {
			$title = trim( (string) $item->get_title() );
			if ( $title === '' ) {
				continue;
			}
			$pub_date  = strtotime( $item->get_date( 'Y-m-d H:i:s' ) );
			$diff_mins = $pub_date ? (int) round( ( $now - $pub_date ) / 60 ) : 0;
			$out[]     = [
				'title'       => $title,
				'permalink'   => (string) $item->get_permalink(),
				'time_label'  => (string) $item->get_date( 'H:i' ),
				'diff_mins'   => $diff_mins,
				'is_fresh'    => ( $diff_mins > 0 && $diff_mins <= 60 ),
			];
		}
		return $out;
	}
}

if ( ! function_exists( 'newsai_google_news_get_items' ) ) {
	/**
	 * @param string $geo   Geo code.
	 * @param string $beat  Author beat.
	 * @param int    $limit Max items.
	 * @return array{items:list<array>,stale:bool,circuit:bool}|\WP_Error
	 */
	function newsai_google_news_get_items( $geo, $beat = '', $limit = 8 ) {
		if ( ! function_exists( 'newsai_news_edition_for_geo' ) ) {
			return new WP_Error( 'news_module', __( 'News module unavailable.', 'newsroom-ai-assistant' ) );
		}

		$beat     = newsai_google_news_normalize_beat( $beat );
		$edition  = newsai_news_edition_for_geo( $geo );
		$identity = newsai_google_news_feed_identity( $geo, $beat );
		$limit    = max( 1, min( 20, (int) $limit ) );
		$circuit  = newsai_google_news_is_circuit_open( $identity );
		$skip     = $circuit || (bool) get_transient( newsai_google_news_negative_key( $identity ) );

		if ( $skip ) {
			$stale = newsai_google_news_get_stale_items( $identity );
			if ( $stale !== [] ) {
				return [
					'items'   => array_slice( $stale, 0, $limit ),
					'stale'   => true,
					'circuit' => $circuit,
				];
			}
			return new WP_Error(
				'news_circuit_open',
				__( 'Google News is temporarily unavailable. Please try again later.', 'newsroom-ai-assistant' ),
				[ 'circuit' => $circuit ]
			);
		}

		$rss = newsai_google_news_fetch_rss( $edition, $beat );
		if ( is_wp_error( $rss ) ) {
			newsai_google_news_record_failure( $identity, $rss->get_error_code() );
			$stale = newsai_google_news_get_stale_items( $identity );
			if ( $stale !== [] ) {
				return [
					'items'   => array_slice( $stale, 0, $limit ),
					'stale'   => true,
					'circuit' => newsai_google_news_is_circuit_open( $identity ),
				];
			}
			return $rss;
		}

		$items = newsai_google_news_parse_feed_items( $rss, $limit );
		if ( $items === [] ) {
			newsai_google_news_record_failure( $identity, 'empty_feed' );
			$stale = newsai_google_news_get_stale_items( $identity );
			if ( $stale !== [] ) {
				return [
					'items'   => array_slice( $stale, 0, $limit ),
					'stale'   => true,
					'circuit' => newsai_google_news_is_circuit_open( $identity ),
				];
			}
			return new WP_Error(
				'news_empty',
				__( 'No news headlines were returned.', 'newsroom-ai-assistant' )
			);
		}

		newsai_google_news_record_success( $identity );
		newsai_google_news_store_stale_items( $identity, $items );

		return [
			'items'   => $items,
			'stale'   => false,
			'circuit' => false,
		];
	}
}

if ( ! function_exists( 'newsai_google_news_build_sidebar_html' ) ) {
	/**
	 * @param list<array{title:string,permalink:string,time_label:string,diff_mins:int,is_fresh:bool}> $items  Items.
	 * @param string                                                                                   $beat   Beat slug.
	 * @param bool                                                                                     $stale  Stale cache flag.
	 * @param string                                                                                   $context desk|editor.
	 * @return string
	 */
	function newsai_google_news_build_sidebar_html( array $items, $beat = '', $stale = false, $context = 'desk' ) {
		$is_gr    = function_exists( 'newsai_get_ui_lang' ) && newsai_get_ui_lang() === 'el';
		$min_txt  = $is_gr ? 'λ πριν' : 'm ago';
		$html     = '';

		if ( $stale ) {
			$notice = $is_gr
				? 'Εμφανίζονται αποθηκευμένα νέα (προσωρινή διακοπή σύνδεσης με Google News).'
				: 'Showing cached headlines (Google News temporarily unreachable).';
			$html .= '<p class="nros-radar-stale-notice">' . esc_html( $notice ) . '</p>';
		}

		$beat = newsai_google_news_normalize_beat( $beat );
		if ( $beat !== '' ) {
			$beat_labels = [
				's' => 'Sports',
				'b' => 'Business',
				'e' => 'Entertainment',
				'm' => 'Health',
				't' => 'Science & Tech',
			];
			$label = isset( $beat_labels[ $beat ] ) ? $beat_labels[ $beat ] : ( $is_gr ? 'Προσωποποιημένο' : 'Personalized' );
			$html .= '<div class="nros-radar-news-beat">🎯 ' . esc_html( $label ) . ' News</div>';
		}

		foreach ( $items as $item ) {
			$diff_mins = (int) $item['diff_mins'];
			if ( ! empty( $item['is_fresh'] ) ) {
				$badge_class = 'nros-radar-badge nros-radar-badge--fresh';
				$badge_text  = '⚡ FRESH (' . esc_html( (string) $diff_mins ) . esc_html( $min_txt ) . ')';
			} else {
				$badge_class = 'nros-radar-badge nros-radar-badge--muted';
				$badge_text  = '📰 ' . esc_html( (string) $item['time_label'] );
			}

			$html .= '<div class="nros-radar-row">';
			$html .= '<div class="nros-radar-row__body">';
			$html .= '<span class="' . esc_attr( $badge_class ) . '">' . $badge_text . '</span>';
			$html .= '<a href="' . esc_url( $item['permalink'] ) . '" target="_blank" rel="noopener noreferrer" class="nros-radar-row__link" data-trend-term="' . esc_attr( $item['title'] ) . '">' . esc_html( $item['title'] ) . '</a>';
			$html .= '</div>';
			$html .= function_exists( 'newsai_radar_row_action_html' )
				? newsai_radar_row_action_html( $item['title'], $context )
				: '';
			$html .= '</div>';
		}

		return $html;
	}
}

if ( ! function_exists( 'newsai_google_news_get_sidebar_html' ) ) {
	/**
	 * @param string $geo  Geo code.
	 * @param string $beat Author beat.
	 * @param string $context desk|editor.
	 * @return array{html:string,stale:bool}|\WP_Error
	 */
	function newsai_google_news_get_sidebar_html( $geo, $beat = '', $context = 'desk' ) {
		$beat     = newsai_google_news_normalize_beat( $beat );
		$identity = newsai_google_news_feed_identity( $geo, $beat );
		$result   = newsai_google_news_get_items( $geo, $beat, 8 );

		if ( is_wp_error( $result ) ) {
			$stale_html = get_transient( newsai_google_news_stale_html_key( $identity ) );
			if ( is_string( $stale_html ) && $stale_html !== '' ) {
				return [
					'html'  => $stale_html,
					'stale' => true,
				];
			}
			return $result;
		}

		$html = newsai_google_news_build_sidebar_html( $result['items'], $beat, ! empty( $result['stale'] ), $context );
		if ( empty( $result['stale'] ) && $html !== '' ) {
			set_transient(
				newsai_google_news_stale_html_key( $identity ),
				newsai_google_news_build_sidebar_html( $result['items'], $beat, true, 'desk' ),
				NEWSAI_NEWS_STALE_TTL
			);
		}

		return [
			'html'  => $html,
			'stale' => ! empty( $result['stale'] ),
		];
	}
}

if ( ! function_exists( 'newsai_google_news_ajax_cache_key' ) ) {
	/**
	 * Matches legacy transient key for editor HTML cache.
	 *
	 * @param string $geo  Geo code.
	 * @param string $beat Author beat.
	 * @return string
	 */
	function newsai_google_news_ajax_cache_key( $geo, $beat = '', $context = 'desk' ) {
		$edition = newsai_news_edition_for_geo( $geo );
		$beat    = newsai_google_news_normalize_beat( $beat );
		$context = ( $context === 'editor' ) ? 'editor' : 'desk';
		return 'nros_news_html_v7_' . $context . '_' . strtoupper( (string) $edition['gl'] ) . '_'
			. ( $beat !== '' ? $beat : 'general' ) . '_'
			. sanitize_key( (string) $edition['ceid_tail'] );
	}
}
