<?php

namespace SerpIQ\Api\Controllers;

use SerpIQ\Content\Question_Queries;
use SerpIQ\Helpers\Options;

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

/**
 * REST: Question Queries (Milestone 2).
 *
 * Exposes Question_Queries backend model via:
 * - GET /question-queries        (list + overview, paginated)
 * - GET /question-queries/terms  (read configured terms)
 * - POST /question-queries/terms (update configured terms)
 */
final class Question_Queries_Controller {

	private static ?Question_Queries_Controller $instance = null;

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

	public function register_routes(): void {
		register_rest_route(
			'serpiq/v1',
			'/question-queries',
			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' ) ),
					'search'    => array( 'required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ),
					'score_min' => array( 'required' => false, 'type' => 'integer', 'default' => 0, 'sanitize_callback' => 'absint' ),
					'score_max' => array( 'required' => false, 'type' => 'integer', 'default' => 100, 'sanitize_callback' => 'absint' ),
					'page'      => array( 'required' => false, 'type' => 'integer', 'default' => 1, 'sanitize_callback' => 'absint' ),
					'per_page'  => array( 'required' => false, 'type' => 'integer', 'default' => 25, 'sanitize_callback' => 'absint' ),
				),
			)
		);

		register_rest_route(
			'serpiq/v1',
			'/question-queries/terms',
			array(
				array(
					'methods'             => 'GET',
					'callback'            => array( $this, 'get_terms' ),
					'permission_callback' => array( \SerpIQ\Api\Rest_Permission::class, 'admin_rest_request' ),
				),
				array(
					'methods'             => 'POST',
					'callback'            => array( $this, 'update_terms' ),
					'permission_callback' => array( \SerpIQ\Api\Rest_Permission::class, 'admin_rest_request' ),
					'args'                => array(
						'terms' => array(
							'required'          => true,
							'type'              => 'string',
							'sanitize_callback' => 'sanitize_textarea_field',
						),
					),
				),
			)
		);
	}

	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 Question Queries 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' ) );
		}

		// Load configured question terms from option.
		$option_value = (string) get_option( Options::question_terms_key(), '' );
		$terms        = $this->parse_terms_string( $option_value );

		$search     = sanitize_text_field( (string) $request->get_param( 'search' ) );
		$score_min_raw = (string) $request->get_param( 'score_min' );
		$score_max_raw = (string) $request->get_param( 'score_max' );

		$score_min = ( '' === trim( $score_min_raw ) ) ? 0 : absint( $score_min_raw );
		$score_max = ( '' === trim( $score_max_raw ) ) ? 100 : absint( $score_max_raw );
		$score_min = min( 100, $score_min );
		$score_max = min( 100, $score_max );
		if ( $score_max < $score_min ) {
			$tmp = $score_min;
			$score_min = $score_max;
			$score_max = $tmp;
		}

		$args = array(
			'terms'     => $terms,
			'search'    => $search,
			'score_min' => $score_min,
			'score_max' => $score_max,
		);

		$result = Question_Queries::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();

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

		$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
		);
	}

	/**
	 * Return current question terms (from option), plus defaults as reference.
	 */
	public function get_terms( \WP_REST_Request $request ): \WP_REST_Response {
		unset( $request );

		$stored      = (string) get_option( Options::question_terms_key(), '' );
		$stored_list = $this->parse_terms_string( $stored );

		// Simple defaults, matching backend model.
		$defaults = array( 'how', 'what', 'where', 'when', 'why', 'who' );

		return new \WP_REST_Response(
			array(
				'terms'        => $stored_list,
				'raw'          => $stored,
				'defaultTerms' => $defaults,
			),
			200
		);
	}

	/**
	 * Update question terms from POST body.
	 */
	public function update_terms( \WP_REST_Request $request ): \WP_REST_Response {
		$raw_terms = $request->get_param( 'terms' );

		// Accept either comma-separated string or array of strings.
		$list = array();
		if ( is_array( $raw_terms ) ) {
			foreach ( $raw_terms as $t ) {
				$list[] = (string) $t;
			}
		} else {
			$list = $this->parse_terms_string( (string) $raw_terms );
		}

		// Normalize and join back into comma-separated string for storage.
		$normalized = array();
		foreach ( $list as $t ) {
			$t = trim( (string) $t );
			if ( '' === $t ) {
				continue;
			}
			$normalized[ $t ] = true;
		}

		$final_terms = implode( ', ', array_keys( $normalized ) );
		update_option( Options::question_terms_key(), $final_terms, false );

		return $this->get_terms( $request );
	}

	/**
	 * Parse a comma-separated string of terms into an array.
	 *
	 * @param string $value
	 * @return array<int,string>
	 */
	private function parse_terms_string( string $value ): array {
		if ( '' === trim( $value ) ) {
			return array();
		}
		$parts = preg_split( '/,/', $value );
		$out   = array();
		foreach ( (array) $parts as $p ) {
			$t = trim( (string) $p );
			if ( '' === $t ) {
				continue;
			}
			$out[ $t ] = true;
		}
		return array_keys( $out );
	}

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

