<?php

namespace SerpIQ\Api\Controllers;

use SerpIQ\Content\Content_Refresh;

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

/**
 * REST: Content Refresh report (Milestone 2).
 *
 * Exposes Content_Refresh backend model via:
 * - GET /content-refresh        (list + overview, paginated)
 * - GET /content-refresh/detail (single page by post_id)
 */
final class Content_Refresh_Controller {
	private static ?Content_Refresh_Controller $instance = null;

	public static function instance(): Content_Refresh_Controller {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public function register_routes(): void {
		register_rest_route(
			'serpiq/v1',
			'/content-refresh',
			array(
				'methods'             => 'GET',
				'callback'            => array( $this, 'list_items' ),
				'permission_callback' => array( \SerpIQ\Api\Rest_Permission::class, 'admin_rest_request' ),
				'args'                => array(
					'period'                  => array( 'required' => false, 'type' => 'integer', 'default' => 28, 'sanitize_callback' => 'absint', 'enum' => array( 28, 90 ) ),
					'end'                     => array( 'required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => array( $this, 'validate_date_arg' ) ),
					'priority'                => array( 'required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_key' ),
					'post_type'               => array( 'required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_key' ),
					'min_age_days'            => array( 'required' => false, 'type' => 'integer', 'sanitize_callback' => 'absint' ),
					'min_freshness_gap_days'  => array( 'required' => false, 'type' => 'integer', 'sanitize_callback' => 'absint' ),
					'min_impressions_current' => array( 'required' => false, 'type' => 'integer', 'sanitize_callback' => 'absint' ),
					'min_clicks_total'        => array( 'required' => false, 'type' => 'integer', 'sanitize_callback' => 'absint' ),
					'page'                    => array( 'required' => false, 'type' => 'integer', 'default' => 1, 'sanitize_callback' => 'absint' ),
					'per_page'                => array( 'required' => false, 'type' => 'integer', 'default' => 20, 'sanitize_callback' => 'absint' ),
				),
			)
		);

		register_rest_route(
			'serpiq/v1',
			'/content-refresh/detail',
			array(
				'methods'             => 'GET',
				'callback'            => array( $this, 'detail' ),
				'permission_callback' => array( \SerpIQ\Api\Rest_Permission::class, 'admin_rest_request' ),
				'args'                => array(
					'post_id' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
					),
					'period'  => array( 'required' => false, 'type' => 'integer', 'default' => 28, 'sanitize_callback' => 'absint', 'enum' => array( 28, 90 ) ),
					'end'     => array( 'required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => array( $this, 'validate_date_arg' ) ),
				),
			)
		);
	}

	public function validate_date_arg( mixed $value, \WP_REST_Request $request, string $param ): bool {
		unset( $request, $param );
		$v = is_scalar( $value ) ? (string) $value : '';
		$v = trim( $v );
		if ( '' === $v ) {
			return true;
		}
		return (bool) preg_match( '/^\d{4}-\d{2}-\d{2}$/', $v );
	}

	/**
	 * List Content Refresh candidates with overview and pagination.
	 */
	public function list_items( \WP_REST_Request $request ): \WP_REST_Response {
		$period = absint( $request->get_param( 'period' ) );
		if ( ! in_array( $period, array( 28, 90 ), true ) ) {
			$period = 28;
		}

		$end_param = (string) $request->get_param( 'end' );
		$end_date  = $this->sanitize_date( $end_param );
		if ( ! $end_date ) {
			$end_date = gmdate( 'Y-m-d', strtotime( '-1 day' ) );
		}

		$args = array(
			'min_age_days'            => absint( $request->get_param( 'min_age_days' ) ),
			'min_freshness_gap_days'  => absint( $request->get_param( 'min_freshness_gap_days' ) ),
			'min_impressions_current' => absint( $request->get_param( 'min_impressions_current' ) ),
			'min_clicks_total'        => absint( $request->get_param( 'min_clicks_total' ) ),
		);

		$result = Content_Refresh::instance()->compute( $end_date, $period, $args );

		$items    = isset( $result['items'] ) && is_array( $result['items'] ) ? $result['items'] : array();
		$overview = isset( $result['overview'] ) && is_array( $result['overview'] ) ? $result['overview'] : array();

		// Optional filters on the returned items (post_type, priority).
		$priority_filter = sanitize_key( (string) $request->get_param( 'priority' ) );
		if ( '' !== $priority_filter ) {
			$items = array_values(
				array_filter(
					$items,
					static function ( $row ) use ( $priority_filter ) {
						return isset( $row['priority_level'] ) && $row['priority_level'] === $priority_filter;
					}
				)
			);
		}

		$post_type_filter = (string) $request->get_param( 'post_type' );
		if ( '' !== $post_type_filter ) {
			$items = array_values(
				array_filter(
					$items,
					static function ( $row ) use ( $post_type_filter ) {
						return isset( $row['post_type'] ) && $row['post_type'] === $post_type_filter;
					}
				)
			);
		}

		// Pagination.
		$page     = max( 1, absint( $request->get_param( 'page' ) ) );
		$per_page = absint( $request->get_param( 'per_page' ) );
		if ( $per_page <= 0 ) {
			$per_page = 20;
		} elseif ( $per_page > 100 ) {
			$per_page = 100;
		}

		$total      = count( $items );
		$total_page = $per_page > 0 ? (int) ceil( $total / $per_page ) : 1;
		$offset     = ( $page - 1 ) * $per_page;

		if ( $offset < 0 ) {
			$offset = 0;
		}

		$paged_items = array_slice( $items, $offset, $per_page );

		$overview['total']      = $total;
		$overview['page']       = $page;
		$overview['perPage']    = $per_page;
		$overview['totalPages'] = $total_page;

		return new \WP_REST_Response(
			array(
				'overview' => $overview,
				'items'    => $paged_items,
			),
			200
		);
	}

	/**
	 * Detail for a single post (reuses compute and filters by post_id).
	 */
	public function detail( \WP_REST_Request $request ): \WP_REST_Response {
		$post_id = (int) $request->get_param( 'post_id' );
		if ( $post_id <= 0 ) {
			return new \WP_REST_Response(
				array(
					'error' => 'Invalid post_id',
				),
				400
			);
		}

		$period = (int) $request->get_param( 'period' );
		if ( ! in_array( $period, array( 28, 90 ), true ) ) {
			$period = 28;
		}

		$end_param = (string) $request->get_param( 'end' );
		$end_date  = $this->sanitize_date( $end_param );
		if ( ! $end_date ) {
			$end_date = gmdate( 'Y-m-d', strtotime( '-1 day' ) );
		}

		$result = Content_Refresh::instance()->compute( $end_date, $period, array() );
		$items  = isset( $result['items'] ) && is_array( $result['items'] ) ? $result['items'] : array();

		$found = null;
		foreach ( $items as $row ) {
			if ( isset( $row['post_id'] ) && (int) $row['post_id'] === $post_id ) {
				$found = $row;
				break;
			}
		}

		if ( null === $found ) {
			return new \WP_REST_Response(
				array(
					'error' => 'Post not found in Content Refresh candidates for this period.',
				),
				404
			);
		}

		$overview = isset( $result['overview'] ) && is_array( $result['overview'] ) ? $result['overview'] : array();

		// Enrich detail: top queries and daily trend for this page.
		$extra = $this->get_detail_extras_for_item( $found, $overview );

		return new \WP_REST_Response(
			array(
				'item'        => $found,
				'overview'    => $overview,
				'top_queries' => $extra['top_queries'],
				'trend'       => $extra['trend'],
			),
			200
		);
	}

	/**
	 * Build additional detail information (top queries, daily trend) for a given item.
	 *
	 * @param array $item     Item from Content_Refresh::compute().
	 * @param array $overview Overview from Content_Refresh::compute().
	 * @return array{top_queries:array<int,array<string,mixed>>,trend:array<int,array<string,mixed>>}
	 */
	private function get_detail_extras_for_item( array $item, array $overview ): array {
		global $wpdb;

		$page_url = isset( $item['page_url'] ) ? (string) $item['page_url'] : '';
		if ( '' === $page_url ) {
			return array(
				'top_queries' => array(),
				'trend'       => array(),
			);
		}

		$prefix = $wpdb->prefix;
		$table_pages   = $prefix . 'serpiq_pages';
		$table_queries = $prefix . 'serpiq_queries';

		$current_range  = isset( $overview['currentRange'] ) && is_array( $overview['currentRange'] ) ? $overview['currentRange'] : array();
		$previous_range = isset( $overview['previousRange'] ) && is_array( $overview['previousRange'] ) ? $overview['previousRange'] : array();

		$start = isset( $previous_range['start'] ) ? (string) $previous_range['start'] : '';
		$end   = isset( $current_range['end'] ) ? (string) $current_range['end'] : '';

		if ( '' === $start || '' === $end ) {
			$end   = gmdate( 'Y-m-d', strtotime( '-1 day' ) );
			$start = gmdate( 'Y-m-d', strtotime( '-90 day' ) );
		}

		// Daily trend for this page: clicks and impressions by date.
		$sql_trend = "
			SELECT
				date,
				COALESCE(SUM(clicks),0) AS clicks,
				COALESCE(SUM(impressions),0) AS impressions
			FROM %i
			WHERE page_url = %s
			  AND date BETWEEN %s AND %s
			GROUP BY date
			ORDER BY date ASC
		";
		$trend = $wpdb->get_results(
			$wpdb->prepare( $sql_trend, array_merge( array( $table_pages ), array( $page_url, $start, $end ) ) ),
			ARRAY_A
		);
		$trend = is_array( $trend ) ? $trend : array();

		// Top queries for this page in the combined period.
		$sql_queries = "
			SELECT
				query,
				COALESCE(SUM(clicks),0) AS clicks,
				COALESCE(SUM(impressions),0) AS impressions,
				CASE WHEN COALESCE(SUM(impressions),0) > 0
					THEN (COALESCE(SUM(clicks),0) / COALESCE(SUM(impressions),0))
					ELSE 0 END AS ctr,
				CASE WHEN COALESCE(SUM(impressions),0) > 0
					THEN (COALESCE(SUM(position * impressions),0) / COALESCE(SUM(impressions),0))
					ELSE 0 END AS position
			FROM %i
			WHERE page_url = %s
			  AND date BETWEEN %s AND %s
			GROUP BY query
			ORDER BY clicks DESC
			LIMIT 10
		";
		$top_queries = $wpdb->get_results(
			$wpdb->prepare( $sql_queries, array_merge( array( $table_queries ), array( $page_url, $start, $end ) ) ),
			ARRAY_A
		);
		$top_queries = is_array( $top_queries ) ? $top_queries : array();

		return array(
			'top_queries' => $top_queries,
			'trend'       => $trend,
		);
	}

	private function sanitize_date( string $date ): ?string {
		if ( '' === $date ) {
			return null;
		}
		return preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) ? $date : null;
	}
}

