<?php

namespace WPDRMS\ASP\AI\Service;

use RuntimeException;
use WPDRMS\ASP\AI\AbstractCachedAIFeatureService;
use WPDRMS\ASP\AI\AIInsufficientDataException;
use WPDRMS\ASP\Priorities\PrioritiesService;
use WPDRMS\ASP\Statistics\ORM\Result as StatResult;
use WPDRMS\ASP\Statistics\ORM\Search as StatSearch;
use WPDRMS\PluginCore\Traits\SingletonTrait;

if ( ! defined( 'ABSPATH' ) ) {
	die( '-1' );
}

class AIPrioritiesAdvisorService extends AbstractCachedAIFeatureService {
	use SingletonTrait;

	private function __construct() {}

	/**
	 * @param int  $asp_id  0 = all instances
	 * @param bool $force
	 * @return array<string, mixed>
	 */
	public function analyze( int $asp_id, bool $force = false ): array {
		try {
			return $this->runCached( array( 'asp_id' => $asp_id ), $force );
		} catch ( AIInsufficientDataException $e ) {
			return array(
				'report'       => null,
				'issues'       => array(),
				'generated_at' => null,
				'cached'       => false,
				'tokens_used'  => 0,
			);
		}
	}

	protected function getFeatureSlug(): string {
		return 'priorities_advisor';
	}

	protected function getAgentId(): string {
		return 'priorities_advisor';
	}

	protected function getTtl(): int {
		return DAY_IN_SECONDS;
	}

	protected function buildCacheKey( array $args ): string {
		return md5( (string) $args['asp_id'] );
	}

	protected function collectData( array $args ): array {
		return $this->buildPriorityAdvisorPacket( $args['asp_id'] );
	}

	protected function validateData( array $data ): void {
		if ( count( $data['issues'] ) === 0 && count( $data['phrase_types'] ) < 3 ) {
			throw new AIInsufficientDataException( 'Insufficient data for priorities advisor.' );
		}
	}

	protected function buildPrompt( array $data ): string {
		return $this->buildPriorityAdvisorPrompt( $data['groups_summary'], $data['issues'], $data['phrase_types'] );
	}

	protected function buildPayload( array $data, string $ai_result ): array {
		return array(
			'report'       => $ai_result,
			'issues'       => $data['issues'],
			'generated_at' => current_time( 'mysql' ),
		);
	}

	/**
	 * Assembles the data packet: health issues + phrase/post-type breakdown + groups summary.
	 *
	 * @param int $asp_id  0 = all instances
	 * @return array{issues: array, phrase_types: array, groups_summary: array}
	 */
	private function buildPriorityAdvisorPacket( int $asp_id ): array {
		global $wpdb;

		// --- 1. Health issues -------------------------------------------------------
		$groups = PrioritiesService::instance()->getSorted();
		$issues = array();

		foreach ( (array) $groups as $group ) {
			$group_name   = (string) ( $group['name'] ?? '' );
			$phrase       = (string) ( $group['phrase'] ?? '' );
			$phrase_logic = (string) ( $group['phrase_logic'] ?? 'disabled' );
			$rules        = is_array( $group['rules'] ?? null ) ? $group['rules'] : array();

			// Group has no name — hard to identify in the UI
			if ( trim( $group_name ) === '' ) {
				$issues[] = array(
					'group_name' => '(unnamed)',
					'type'       => 'missing_name',
					'count'      => 1,
				);
			}

			// Phrase trigger set but no phrase text
			if ( $phrase_logic !== 'disabled' && $phrase === '' ) {
				$issues[] = array(
					'group_name' => $group_name,
					'type'       => 'phrase_misconfigured',
					'count'      => 1,
				);
			}

			foreach ( $rules as $rule ) {
				$field = (string) ( $rule['field'] ?? '' );

				if ( $field === 'tax' ) {
					$taxonomy = (string) ( $rule['values']['taxonomy'] ?? '' );
					$term_ids = is_array( $rule['values']['term_ids'] ?? null ) ? array_map( 'intval', $rule['values']['term_ids'] ) : array();

					if ( empty( $term_ids ) ) {
						$issues[] = array(
							'group_name' => $group_name,
							'type'       => 'empty_rule',
							'count'      => 1,
						);
					} elseif ( $taxonomy !== '' ) {
						$valid    = get_terms(
							array(
								'taxonomy'   => $taxonomy,
								'include'    => $term_ids,
								'fields'     => 'ids',
								'hide_empty' => false,
							)
						);
						$valid    = is_array( $valid ) ? array_map( 'intval', $valid ) : array();
						$orphaned = array_diff( $term_ids, $valid );
						if ( ! empty( $orphaned ) ) {
							$issues[] = array(
								'group_name' => $group_name,
								'type'       => 'orphaned_terms',
								'count'      => count( $orphaned ),
								'taxonomy'   => $taxonomy,
							);
						}
					}
				} elseif ( $field === 'post' ) {
					$post_ids = is_array( $rule['values']['post_ids'] ?? null ) ? array_map( 'intval', $rule['values']['post_ids'] ) : array();

					if ( empty( $post_ids ) ) {
						$issues[] = array(
							'group_name' => $group_name,
							'type'       => 'empty_rule',
							'count'      => 1,
						);
					} else {
						$existing_ids   = get_posts(
							array(
								'post__in'       => $post_ids,
								'posts_per_page' => -1,
								'fields'         => 'ids',
								'post_status'    => 'any',
								'post_type'      => 'any',
							)
						);
						$orphaned_count = count( array_diff( $post_ids, is_array( $existing_ids ) ? array_map( 'intval', $existing_ids ) : array() ) );
						if ( $orphaned_count > 0 ) {
							$issues[] = array(
								'group_name' => $group_name,
								'type'       => 'orphaned_posts',
								'count'      => $orphaned_count,
							);
						}
					}
				}
			}
		}

		// --- 2. Top phrases with post-type breakdown --------------------------------
		$stat_table    = StatSearch::getTableName();
		$result_table  = StatResult::getTableName();
		$asp_id_clause = $asp_id > 0 ? $wpdb->prepare( ' AND s.asp_id = %d', $asp_id ) : '';

		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
		$rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
			"SELECT s.phrase, p.post_type, COUNT(*) AS cnt
			 FROM `{$stat_table}` s
			 JOIN `{$result_table}` r ON r.search_id = s.id AND r.result_type = 1
			 JOIN `{$wpdb->posts}` p ON p.ID = r.result_id
			 WHERE s.found_results > 0
			   AND s.date > DATE_SUB(NOW(), INTERVAL 30 DAY)
			   {$asp_id_clause}
			 GROUP BY s.phrase, p.post_type
			 ORDER BY cnt DESC
			 LIMIT 300",
			ARRAY_A
		) ?? array();
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared

		// Aggregate: phrase → [post_type → count]
		$phrase_map = array();
		foreach ( $rows as $row ) {
			$ph = (string) $row['phrase'];
			$pt = (string) $row['post_type'];
			if ( ! isset( $phrase_map[ $ph ] ) ) {
				$phrase_map[ $ph ] = array( '_total' => 0 );
			}
			$phrase_map[ $ph ][ $pt ]     = ( $phrase_map[ $ph ][ $pt ] ?? 0 ) + (int) $row['cnt'];
			$phrase_map[ $ph ]['_total'] += (int) $row['cnt'];
		}

		// Sort by total, take top 20
		uasort(
			$phrase_map,
			static function ( $a, $b ) {
				return (int) $b['_total'] - (int) $a['_total'];
			}
		);
		$phrase_types = array();
		foreach ( array_slice( $phrase_map, 0, 20, true ) as $phrase => $counts ) {
			$pt_only = $counts;
			unset( $pt_only['_total'] );
			$phrase_types[] = array(
				'phrase'     => $phrase,
				'total'      => $counts['_total'],
				'post_types' => $pt_only,
			);
		}

		// --- 3. Compact groups summary ----------------------------------------------
		$groups_summary = array();
		foreach ( (array) $groups as $group ) {
			$pl               = (string) ( $group['phrase_logic'] ?? 'disabled' );
			$groups_summary[] = array(
				'name'        => (string) ( $group['name'] ?? '' ),
				'phrase'      => (string) ( $group['phrase'] ?? '' ),
				'trigger'     => $pl === 'disabled' ? 'always_on' : $pl,
				'instance'    => (int) ( $group['instance'] ?? 0 ),
				'enabled'     => (bool) ( $group['enabled'] ?? true ),
				'rules_count' => count( is_array( $group['rules'] ?? null ) ? $group['rules'] : array() ),
			);
		}

		return array(
			'issues'         => $issues,
			'phrase_types'   => $phrase_types,
			'groups_summary' => $groups_summary,
		);
	}

	/**
	 * @param array $groups_summary
	 * @param array $issues
	 * @param array $phrase_types
	 * @return string
	 */
	private function buildPriorityAdvisorPrompt( array $groups_summary, array $issues, array $phrase_types ): string {
		return (string) wp_json_encode(
			array(
				'priority_groups' => $groups_summary,
				'health_issues'   => $issues,
				'top_phrases'     => $phrase_types,
			)
		);
	}
}
