<?php

namespace AIPT\Api\Dashboard;

use AIPT\Api\Traits\RateLimitTrait;
use AIPT\Core\Services\UsageTracker;
use WP_REST_Request;
use WP_REST_Response;

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

class DashboardController {
	use RateLimitTrait;

	private DashboardRepository $repository;

	public function __construct( ?DashboardRepository $repository = null ) {
		$this->repository = $repository ?? new DashboardRepository();
	}

	public function register_routes(): void {
		register_rest_route(
			'aipt/v1',
			'/dashboard/usage-stats',
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_usage_stats' ],
				'permission_callback' => [ $this, 'check_permissions' ],
			]
		);

		register_rest_route(
			'aipt/v1',
			'/dashboard/recent-activity',
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_recent_activity' ],
				'permission_callback' => [ $this, 'check_permissions' ],
				'args'                => [
					'generations_limit' => [
						'type'    => 'integer',
						'default' => 3,
						'minimum' => 1,
						'maximum' => 10,
					],
					'jobs_limit' => [
						'type'    => 'integer',
						'default' => 3,
						'minimum' => 1,
						'maximum' => 10,
					],
					'chatbot_limit' => [
						'type'    => 'integer',
						'default' => 2,
					],
				],
			]
		);

		register_rest_route(
			'aipt/v1',
			'/dashboard/pricing-status',
			array(
				'methods'             => 'GET',
				'callback'            => array( $this, 'get_pricing_status' ),
				'permission_callback' => array( $this, 'check_permissions' ),
			)
		);

		register_rest_route(
			'aipt/v1',
			'/dashboard/refresh-pricing',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'refresh_pricing' ),
				'permission_callback' => array( $this, 'check_permissions_write' ),
			)
		);

		register_rest_route(
			'aipt/v1',
			'/dashboard/widget-order',
			array(
				array(
					'methods'             => 'GET',
					'callback'            => array( $this, 'get_widget_order' ),
					'permission_callback' => array( $this, 'check_permissions' ),
				),
				array(
					'methods'             => 'POST',
					'callback'            => array( $this, 'save_widget_order' ),
					'permission_callback' => array( $this, 'check_permissions_write' ),
					'args'                => array(
						'tools' => array(
							'type'     => 'array',
							'required' => false,
							'items'    => array( 'type' => 'string' ),
						),
						'rail'  => array(
							'type'     => 'array',
							'required' => false,
							'items'    => array( 'type' => 'string' ),
						),
					),
				),
			)
		);
	}

	public function check_permissions(): bool {
		return current_user_can( 'manage_woocommerce' );
	}

	/**
	 * Capability + nonce check for write endpoints. Mirrors the
	 * BaseBulkGeneratorController shape so QA curl scripts can assert on
	 * `rest_invalid_nonce` consistently across the plugin.
	 */
	public function check_permissions_write( $request = null ) {
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			return new \WP_Error(
				'rest_forbidden',
				__( 'You do not have permission to perform this action.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}

		$nonce = '';
		if ( $request instanceof WP_REST_Request ) {
			$nonce = (string) $request->get_header( 'X-WP-Nonce' );
		}
		if ( '' === $nonce && isset( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
			$nonce = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_WP_NONCE'] ) );
		}

		if ( '' === $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
			return new \WP_Error(
				'rest_invalid_nonce',
				__( 'Invalid security token. Please refresh the page and try again.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}
		return true;
	}

	public function get_pricing_status( WP_REST_Request $request ): WP_REST_Response {
		$meta = \AIPT\Core\Services\PricingFeedManager::instance()->get_snapshot_meta();
		return new WP_REST_Response(
			array(
				'success' => true,
				'data'    => $meta,
			),
			200
		);
	}

	public function refresh_pricing( WP_REST_Request $request ) {
		$rate_limit = $this->check_dashboard_rate_limit( 'dashboard_refresh_pricing' );
		if ( $rate_limit instanceof \WP_Error ) {
			return $rate_limit;
		}

		$result = \AIPT\Core\Services\PricingFeedManager::instance()->refresh( false );
		$status = $result['ok'] ? 200 : ( 'locked' === ( $result['status'] ?? '' ) ? 409 : 500 );

		$response = new WP_REST_Response(
			array(
				'success' => $result['ok'],
				'data'    => array_merge(
					$result,
					array( 'meta' => \AIPT\Core\Services\PricingFeedManager::instance()->get_snapshot_meta() )
				),
			),
			$status,
			409 === $status ? array( 'Retry-After' => '60' ) : array()
		);

		return $response;
	}

	public function get_usage_stats( WP_REST_Request $request ) {
		$rate_limit = $this->check_dashboard_rate_limit( 'dashboard_usage_stats' );
		if ( $rate_limit instanceof \WP_Error ) {
			return $rate_limit;
		}

		$usage  = UsageTracker::get_current_month();
		$totals = $usage['totals'] ?? array();

		$approved = (int) ( $totals['approved'] ?? 0 );
		$rejected = (int) ( $totals['rejected'] ?? 0 );
		$divisor  = $approved + $rejected;
		$approval_rate = $divisor > 0 ? round( ( $approved / $divisor ) * 100, 1 ) : 0;

		$generated_this_month = (int) ( $totals['generated'] ?? 0 );
		$applied_this_month = (int) ( $totals['applied'] ?? 0 );

		$cost_micros = (int) ( $totals['cost_micros'] ?? 0 );

		return new WP_REST_Response(
			array(
				'success' => true,
				'data'    => array(
					'generated_this_month' => $generated_this_month,
					'applied_this_month'   => $applied_this_month,
					'approval_rate'        => $approval_rate,
					'approved_count'       => $approved,
					'rejected_count'       => $rejected,
					'pending_count'        => 0,
					'api_calls'            => (int) ( $totals['api_calls'] ?? 0 ),
					'input_tokens'         => (int) ( $totals['input_tokens'] ?? 0 ),
					'output_tokens'        => (int) ( $totals['output_tokens'] ?? 0 ),
					'cost_micros'          => $cost_micros,
					'cost_usd'             => $cost_micros / 1000000,
					'by_provider'          => $this->normalize_provider_breakdown( $usage['api'] ?? array() ),
					'by_tool'              => $this->normalize_tool_breakdown( $usage['tools'] ?? array() ),
				),
			),
			200
		);
	}

	/**
	 * Normalise provider buckets into a list ordered by cost (desc).
	 *
	 * @param array $api Raw api section of the usage payload.
	 * @return array
	 */
	private function normalize_provider_breakdown( array $api ): array {
		$items = array();
		foreach ( $api as $provider => $bucket ) {
			if ( ! is_array( $bucket ) ) {
				continue;
			}
			$items[] = array(
				'provider'      => sanitize_key( (string) $provider ),
				'calls'         => (int) ( $bucket['calls'] ?? 0 ),
				'input_tokens'  => (int) ( $bucket['input_tokens'] ?? 0 ),
				'output_tokens' => (int) ( $bucket['output_tokens'] ?? 0 ),
				'cost_micros'   => (int) ( $bucket['cost_micros'] ?? 0 ),
			);
		}
		usort( $items, static function ( $a, $b ) {
			return $b['cost_micros'] <=> $a['cost_micros'];
		} );
		return $items;
	}

	/**
	 * Normalise tool buckets into a list ordered by total events (desc).
	 *
	 * @param array $tools Raw tools section of the usage payload.
	 * @return array
	 */
	private function normalize_tool_breakdown( array $tools ): array {
		$items = array();
		foreach ( $tools as $tool => $events ) {
			if ( ! is_array( $events ) ) {
				continue;
			}
			$generated = (int) ( $events['generated'] ?? 0 );
			$applied   = (int) ( $events['applied'] ?? 0 );
			$approved  = (int) ( $events['approved'] ?? 0 );
			$rejected  = (int) ( $events['rejected'] ?? 0 );
			$items[] = array(
				'tool'      => sanitize_key( (string) $tool ),
				'generated' => $generated,
				'applied'   => $applied,
				'approved'  => $approved,
				'rejected'  => $rejected,
				'total'     => $generated + $applied + $approved + $rejected,
			);
		}
		usort( $items, static function ( $a, $b ) {
			return $b['total'] <=> $a['total'];
		} );
		return $items;
	}

	public function get_recent_activity( WP_REST_Request $request ) {
		$rate_limit = $this->check_dashboard_rate_limit( 'dashboard_recent_activity' );
		if ( $rate_limit instanceof \WP_Error ) {
			return $rate_limit;
		}

		$gen_limit     = max( 1, min( 10, (int) $request->get_param( 'generations_limit' ) ) );
		$jobs_limit    = max( 1, min( 10, (int) $request->get_param( 'jobs_limit' ) ) );
		$chatbot_limit_param = $request->get_param( 'chatbot_limit' );
		$chatbot_limit       = null === $chatbot_limit_param
			? 2
			: max( 1, min( 10, (int) $chatbot_limit_param ) );

		$recent_events = $this->repository->fetchGenerations( $gen_limit );
		$generations   = array();
		foreach ( $recent_events as $idx => $entry ) {
			$product_id   = isset( $entry['product_id'] ) ? (int) $entry['product_id'] : 0;
			$product_name = '';
			if ( $product_id > 0 ) {
				$product_name = self::decode_entities(
					wp_strip_all_tags( (string) get_the_title( $product_id ) )
				);
			}
			$count = isset( $entry['count'] ) ? max( 1, (int) $entry['count'] ) : 1;

			if ( '' !== $product_name ) {
				$title = $count > 1
					? sprintf( '%s +%d', $product_name, $count - 1 )
					: $product_name;
			} elseif ( $count > 1 ) {
				$title = sprintf(
					/* translators: 1: tool label, 2: item count */
					__( '%1$s × %2$d', 'ai-product-tools' ),
					self::tool_label( (string) ( $entry['tool'] ?? '' ) ),
					$count
				);
			} elseif ( $product_id > 0 ) {
				$title = sprintf( 'Product #%d', $product_id );
			} else {
				$label = isset( $entry['label'] ) ? (string) $entry['label'] : '';
				$title = $label !== '' ? $label : self::tool_label( (string) ( $entry['tool'] ?? '' ) );
			}

			$generation = array(
				'id'           => $idx + 1,
				'type'         => 'generation',
				'title'        => $title,
				'status'       => sanitize_text_field( (string) ( $entry['event'] ?? 'generated' ) ),
				'created_at'   => sanitize_text_field( (string) ( $entry['created_at'] ?? '' ) ),
				'product_id'   => $product_id,
				'product_name' => $product_name,
				'content_type' => sanitize_text_field( (string) ( $entry['tool'] ?? '' ) ),
				'source'       => sanitize_text_field( (string) ( $entry['tool'] ?? '' ) ),
				'count'        => $count,
			);

			if ( isset( $entry['api'] ) && is_array( $entry['api'] ) && ( $entry['api']['calls'] ?? 0 ) > 0 ) {
				$generation['api'] = array(
					'calls'         => (int) ( $entry['api']['calls'] ?? 0 ),
					'input_tokens'  => (int) ( $entry['api']['input_tokens'] ?? 0 ),
					'output_tokens' => (int) ( $entry['api']['output_tokens'] ?? 0 ),
					'cost_micros'   => (int) ( $entry['api']['cost_micros'] ?? 0 ),
				);
			}

			$generations[] = $generation;
		}

		$chatbot_available = true;
		$chatbot_locked    = false;

		$chatbot     = array();
		$raw_chatbot = $this->repository->fetchChatbotActivity( $chatbot_limit );

		foreach ( (array) $raw_chatbot as $row ) {
			$status          = sanitize_text_field( (string) $row['status'] );
			$record_type     = sanitize_text_field( (string) ( $row['record_type'] ?? '' ) );
			$user_name       = self::decode_entities( sanitize_text_field( (string) ( $row['user_name'] ?? '' ) ) );
			$message_excerpt = self::decode_entities( sanitize_text_field( (string) ( $row['message_excerpt'] ?? '' ) ) );
			$has_ai_response = ! empty( $row['has_ai_response'] );
			$needs_attention = in_array( $status, array( 'queued', 'retried', 'failed' ), true );
			if ( $needs_attention ) {
				$title = __( 'Chat message awaiting reply', 'ai-product-tools' );
			} elseif ( 'conversation' === $record_type ) {
				$title = __( 'Answered chat message', 'ai-product-tools' );
			} else {
				$title = __( 'Chat message', 'ai-product-tools' );
			}

			$chatbot_item = array(
				'id'              => (int) $row['id'],
				'type'            => 'chatbot',
				'title'           => $title,
				'status'          => $status,
				'created_at'      => sanitize_text_field( (string) $row['created_at'] ),
				'content_type'    => $record_type,
				'has_ai_response' => $has_ai_response,
				'needs_attention' => $needs_attention,
				'source'          => 'chatbot',
			);

			if ( '' !== $user_name ) {
				$chatbot_item['user_name'] = $user_name;
			}

			if ( '' !== $message_excerpt ) {
				$chatbot_item['message_excerpt'] = $message_excerpt;
			}

			$chatbot[] = $chatbot_item;
		}

		$raw_jobs = $this->repository->fetchJobs( $jobs_limit );

		$jobs = [];
		foreach ( (array) $raw_jobs as $row ) {
			$jobs[] = [
				'id'         => (int) $row['id'],
				'type'       => 'job',
				'title'      => self::decode_entities( sanitize_text_field( (string) ( $row['name'] ?? '' ) ) ),
				'status'     => sanitize_text_field( $row['status'] ),
				'created_at' => sanitize_text_field( $row['created_at'] ),
			];
		}

		return new WP_REST_Response(
			array(
				'success' => true,
				'data'    => array(
					'generations'       => $generations,
					'jobs'              => $jobs,
					'chatbot'           => $chatbot,
					'chatbot_available' => $chatbot_available,
					'chatbot_locked'    => $chatbot_locked,
				),
			),
			200
		);
	}

	/**
	 * Decode HTML entities (incl. numeric ones like `&#8217;`) for plain-text
	 * React rendering. WordPress's `get_the_title()` applies `wptexturize`
	 * which converts straight quotes to curly entities; React text nodes
	 * do not auto-decode, so the entity would render literally in the UI.
	 */
	private static function decode_entities( string $value ): string {
		if ( '' === $value ) {
			return '';
		}
		return html_entity_decode( $value, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
	}

	/**
	 * Map an internal tool id to a short user-facing label.
	 */
	private static function tool_label( string $tool ): string {
		$map = array(
			'advanced_desc' => __( 'Description', 'ai-product-tools' ),
			'standard_desc' => __( 'Standard Desc', 'ai-product-tools' ),
			'title'         => __( 'Title', 'ai-product-tools' ),
			'faq'           => __( 'FAQ', 'ai-product-tools' ),
			'seo_meta'      => __( 'SEO Meta', 'ai-product-tools' ),
			'image_alt'     => __( 'Image Alt', 'ai-product-tools' ),
			'taxonomy'      => __( 'Taxonomy', 'ai-product-tools' ),
			'automation'    => __( 'Automation', 'ai-product-tools' ),
		);
		return $map[ $tool ] ?? ucfirst( str_replace( '_', ' ', $tool ) );
	}

	private function check_dashboard_rate_limit( string $scope ) {
		$rate_check = $this->checkRateLimit( (int) \get_current_user_id(), $scope );
		return $rate_check['allowed'] ? null : $this->rateLimitResponse( $rate_check );
	}

	/**
	 * Valid tool IDs for the Toolkit Workspace tool grid.
	 *
	 * Mirrors `WorkspaceToolId` in
	 * `src/Components/Toolkit/Workspace/toolDefinitions.tsx`.
	 *
	 * @return string[]
	 */
	public static function valid_tool_ids(): array {
		return array(
			'advanced',
			'standard',
			'title',
			'seo-meta',
			'image-alt',
			'taxonomy-generator',
			'automation-jobs',
			'faq',
			'chatbot',
		);
	}

	/**
	 * Valid right-rail widget IDs for the Toolkit Workspace dashboard.
	 *
	 * @return string[]
	 */
	public static function valid_rail_ids(): array {
		return array( 'providers', 'coverage', 'activity' );
	}

	/**
	 * Read the persisted widget order for the current user.
	 *
	 * Falls back to the default declared order when the user has not yet
	 * customised the layout. Unknown / stale IDs are filtered out and any
	 * missing valid IDs are appended so the catalog stays in sync when
	 * tools or rails are added/removed in code.
	 *
	 * @return array{tools: string[], rail: string[]}
	 */
	public static function get_user_widget_order( int $user_id ): array {
		$default_tools = self::valid_tool_ids();
		$default_rail  = self::valid_rail_ids();

		if ( $user_id <= 0 ) {
			return array(
				'tools' => $default_tools,
				'rail'  => $default_rail,
			);
		}

		$raw = get_user_meta( $user_id, 'aipt_widget_order', true );
		if ( ! is_array( $raw ) ) {
			$raw = array();
		}

		$tools = isset( $raw['tools'] ) && is_array( $raw['tools'] ) ? $raw['tools'] : array();
		$rail  = isset( $raw['rail'] ) && is_array( $raw['rail'] ) ? $raw['rail'] : array();

		return array(
			'tools' => self::reconcile_order( $tools, $default_tools ),
			'rail'  => self::reconcile_order( $rail, $default_rail ),
		);
	}

	/**
	 * Filter `$saved` to only contain valid IDs and append any valid IDs
	 * missing from the persisted order (preserves catalog completeness).
	 *
	 * @param array<int|string, mixed> $saved Persisted order from user_meta.
	 * @param string[]                 $valid Authoritative valid IDs.
	 * @return string[]
	 */
	private static function reconcile_order( array $saved, array $valid ): array {
		$seen   = array();
		$result = array();
		foreach ( $saved as $id ) {
			$id = is_string( $id ) ? sanitize_key( $id ) : '';
			if ( '' === $id || isset( $seen[ $id ] ) || ! in_array( $id, $valid, true ) ) {
				continue;
			}
			$seen[ $id ]  = true;
			$result[]      = $id;
		}
		foreach ( $valid as $id ) {
			if ( ! isset( $seen[ $id ] ) ) {
				$result[] = $id;
			}
		}
		return $result;
	}

	public function get_widget_order( WP_REST_Request $request ): WP_REST_Response {
		$order = self::get_user_widget_order( (int) get_current_user_id() );
		return new WP_REST_Response(
			array(
				'success' => true,
				'data'    => $order,
			),
			200
		);
	}

	public function save_widget_order( WP_REST_Request $request ) {
		$user_id = (int) get_current_user_id();
		if ( $user_id <= 0 ) {
			return new \WP_Error(
				'rest_forbidden',
				__( 'You must be logged in to save widget order.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}

		$tools_param = $request->get_param( 'tools' );
		$rail_param  = $request->get_param( 'rail' );

		if ( ! is_array( $tools_param ) && ! is_array( $rail_param ) ) {
			return new \WP_Error(
				'rest_invalid_param',
				__( 'Provide at least one of: tools, rail.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$existing = self::get_user_widget_order( $user_id );

		$next = array(
			'tools' => is_array( $tools_param )
				? self::reconcile_order( $tools_param, self::valid_tool_ids() )
				: $existing['tools'],
			'rail'  => is_array( $rail_param )
				? self::reconcile_order( $rail_param, self::valid_rail_ids() )
				: $existing['rail'],
		);

		update_user_meta( $user_id, 'aipt_widget_order', $next );

		return new WP_REST_Response(
			array(
				'success' => true,
				'data'    => $next,
			),
			200
		);
	}

}
