<?php
/**
 * Cached public summaries for long agent-profile fields.
 *
 * The raw profile remains the source of truth and is still indexed as site
 * knowledge. This service only prepares compact prompt-safe text for the
 * frontend system prompt.
 *
 * @package Niwa_AI
 */

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

class Niwa_AI_Agent_Profile_Summary_Service {
	const REFRESH_HOOK      = 'niwa_ai_agent_profile_summary_refresh';
	const SUMMARY_VERSION   = 1;
	const SUMMARY_CHAR_LIMIT = 700;
	const LONG_FIELD_THRESHOLD = 700;

	private $auth_manager;
	private $model_router;
	private $ai_client;

	public function __construct( $auth_manager = null, $model_router = null, $ai_client = null ) {
		$this->auth_manager = $auth_manager;
		$this->model_router = $model_router;
		$this->ai_client    = $ai_client;
	}

	public function register_hooks() {
		add_action( 'update_option_' . Niwa_AI_Constants::OPT_AGENT_PROFILE, array( $this, 'on_profile_updated' ), 10, 3 );
		add_action( self::REFRESH_HOOK, array( $this, 'refresh_profile_summaries' ) );

		if ( class_exists( 'Niwa_AI_Index_Sync' ) ) {
			add_action( Niwa_AI_Index_Sync::SITE_REINDEX_HOOK, array( $this, 'on_site_knowledge_reindex' ), 20 );
		}
	}

	public static function profile_field_map() {
		return array(
			'company_name'      => 'COMPANY',
			'about'             => 'ABOUT',
			'products_services' => 'PRODUCTS_SERVICES',
			'faq'               => 'FAQ',
			'policies'          => 'POLICIES',
			'tone'              => 'TONE_STYLE',
			'dos'               => 'DO_DONT_RULES',
			'coupon_rules'      => 'COUPON_RULES',
			'bundle_rules'      => 'BUNDLE_RULES',
			'human_handoff'     => 'HUMAN_HANDOFF',
			'human_hook_hours'  => 'HUMAN_SUPPORT_HOURS',
			'extra'             => 'ADDITIONAL_INSTRUCTIONS',
		);
	}

	public function on_profile_updated( $old_value, $value, $option = '' ) {
		unset( $option );

		if ( ! is_array( $value ) ) {
			$value = array();
		}

		$changed = self::sync_pending_cache_for_profile( $value );
		if ( $changed ) {
			self::schedule_refresh();
		}
	}

	public function on_site_knowledge_reindex() {
		$profile = get_option( Niwa_AI_Constants::OPT_AGENT_PROFILE, array() );
		if ( ! is_array( $profile ) || empty( $profile ) ) {
			return;
		}

		self::sync_pending_cache_for_profile( $profile );
		self::schedule_refresh();
	}

	public function refresh_profile_summaries() {
		$profile = get_option( Niwa_AI_Constants::OPT_AGENT_PROFILE, array() );
		if ( ! is_array( $profile ) ) {
			$profile = array();
		}

		$cache       = self::normalize_cache( get_option( Niwa_AI_Constants::OPT_AGENT_PROFILE_SUMMARIES, array() ) );
		$old_fields  = isset( $cache['fields'] ) && is_array( $cache['fields'] ) ? $cache['fields'] : array();
		$new_fields  = array();
		$changed     = false;

		foreach ( self::profile_field_map() as $key => $label ) {
			$raw_text = isset( $profile[ $key ] ) && is_string( $profile[ $key ] ) ? $profile[ $key ] : '';
			$text     = self::normalize_public_text( $raw_text );
			$hash     = self::hash_text( $text );
			$chars    = self::text_length( $text );
			$existing = isset( $old_fields[ $key ] ) && is_array( $old_fields[ $key ] ) ? $old_fields[ $key ] : array();

			if ( '' === $text ) {
				$new_fields[ $key ] = self::build_entry( $hash, 0, '', 'empty', '' );
				continue;
			}

			if ( $chars <= self::LONG_FIELD_THRESHOLD ) {
				$new_fields[ $key ] = self::build_entry( $hash, $chars, $text, 'short', '' );
				continue;
			}

			$existing_hash    = isset( $existing['hash'] ) ? (string) $existing['hash'] : '';
			$existing_summary = isset( $existing['summary'] ) ? self::sanitize_summary( $existing['summary'] ) : '';
			$existing_status  = isset( $existing['status'] ) ? (string) $existing['status'] : '';
			if (
				$hash === $existing_hash
				&& '' !== $existing_summary
				&& in_array( $existing_status, array( 'ready', 'fallback' ), true )
			) {
				$new_fields[ $key ] = self::normalize_entry( $existing, $hash, $chars );
				continue;
			}

			$result = $this->generate_llm_summary( $label, $text );
			if ( ! empty( $result['summary'] ) ) {
				$new_fields[ $key ] = self::build_entry( $hash, $chars, $result['summary'], 'ready', '' );
			} else {
				$error = isset( $result['error'] ) ? (string) $result['error'] : 'summary_generation_failed';
				$new_fields[ $key ] = self::build_entry( $hash, $chars, self::build_extractive_summary( $text ), 'fallback', $error );
			}
		}

		if ( $new_fields !== $old_fields || intval( $cache['version'] ) !== self::SUMMARY_VERSION ) {
			$changed = true;
		}

		if ( $changed ) {
			self::save_cache(
				array(
					'version'      => self::SUMMARY_VERSION,
					'generated_at' => self::now(),
					'fields'       => $new_fields,
				)
			);
		}
	}

	public static function resolve_prompt_field_text( $field_key, $raw_text ) {
		$field_key = (string) $field_key;
		$text      = self::normalize_public_text( $raw_text );
		if ( '' === $text ) {
			return '';
		}

		if ( self::text_length( $text ) <= self::LONG_FIELD_THRESHOLD ) {
			return $text;
		}

		$hash  = self::hash_text( $text );
		$cache = self::normalize_cache( get_option( Niwa_AI_Constants::OPT_AGENT_PROFILE_SUMMARIES, array() ) );
		$entry = isset( $cache['fields'][ $field_key ] ) && is_array( $cache['fields'][ $field_key ] )
			? $cache['fields'][ $field_key ]
			: array();

		if (
			! empty( $entry['hash'] )
			&& $hash === (string) $entry['hash']
			&& ! empty( $entry['summary'] )
		) {
			$summary = self::sanitize_summary( $entry['summary'] );
			if ( '' !== $summary ) {
				return $summary;
			}
		}

		$fallback = self::build_extractive_summary( $text );
		self::mark_field_pending( $field_key, $text, $hash, $fallback );
		self::schedule_refresh();

		return $fallback;
	}

	public static function sync_pending_cache_for_profile( $profile ) {
		if ( ! is_array( $profile ) ) {
			$profile = array();
		}

		$cache      = self::normalize_cache( get_option( Niwa_AI_Constants::OPT_AGENT_PROFILE_SUMMARIES, array() ) );
		$old_fields = isset( $cache['fields'] ) && is_array( $cache['fields'] ) ? $cache['fields'] : array();
		$new_fields = $old_fields;
		$changed    = false;

		foreach ( self::profile_field_map() as $key => $label ) {
			unset( $label );

			$text  = isset( $profile[ $key ] ) && is_string( $profile[ $key ] ) ? self::normalize_public_text( $profile[ $key ] ) : '';
			$hash  = self::hash_text( $text );
			$chars = self::text_length( $text );

			if ( '' === $text ) {
				if ( isset( $new_fields[ $key ] ) ) {
					$new_fields[ $key ] = self::build_entry( $hash, 0, '', 'empty', '' );
					$changed = true;
				}
				continue;
			}

			if ( $chars <= self::LONG_FIELD_THRESHOLD ) {
				$new_entry = self::build_entry( $hash, $chars, $text, 'short', '' );
				if ( ! isset( $new_fields[ $key ] ) || self::entry_signature( $new_fields[ $key ] ) !== self::entry_signature( $new_entry ) ) {
					$new_fields[ $key ] = $new_entry;
					$changed = true;
				}
				continue;
			}

			$existing = isset( $new_fields[ $key ] ) && is_array( $new_fields[ $key ] ) ? $new_fields[ $key ] : array();
			$existing_hash = isset( $existing['hash'] ) ? (string) $existing['hash'] : '';
			if ( $hash === $existing_hash && ! empty( $existing['summary'] ) ) {
				continue;
			}

			$new_fields[ $key ] = self::build_entry( $hash, $chars, self::build_extractive_summary( $text ), 'pending', '' );
			$changed = true;
		}

		if ( $changed || intval( $cache['version'] ) !== self::SUMMARY_VERSION ) {
			self::save_cache(
				array(
					'version'      => self::SUMMARY_VERSION,
					'generated_at' => self::now(),
					'fields'       => $new_fields,
				)
			);
		}

		return $changed;
	}

	public static function build_extractive_summary( $text, $limit = self::SUMMARY_CHAR_LIMIT ) {
		$text = self::normalize_public_text( $text );
		$text = self::redact_private_text( $text );
		if ( '' === $text ) {
			return '';
		}

		$limit = max( 120, min( self::SUMMARY_CHAR_LIMIT, intval( $limit ) ) );
		if ( self::text_length( $text ) <= $limit ) {
			return $text;
		}

		$sentences = self::split_sentences( $text );
		if ( empty( $sentences ) ) {
			return self::clip_to_limit( $text, $limit );
		}

		$scored = array();
		foreach ( $sentences as $index => $sentence ) {
			$sentence = trim( (string) $sentence );
			if ( '' === $sentence || ! self::is_public_safe_sentence( $sentence ) ) {
				continue;
			}
			$score = self::capability_score( $sentence );
			$scored[] = array(
				'index'    => intval( $index ),
				'score'    => $score,
				'sentence' => $sentence,
			);
		}

		if ( empty( $scored ) ) {
			return self::clip_to_limit( $text, $limit );
		}

		usort(
			$scored,
			function( $a, $b ) {
				if ( $a['score'] === $b['score'] ) {
					return $a['index'] <=> $b['index'];
				}
				return $b['score'] <=> $a['score'];
			}
		);

		$selected = array();
		foreach ( $scored as $candidate ) {
			if ( $candidate['score'] <= 0 && ! empty( $selected ) ) {
				continue;
			}
			$selected[] = $candidate['sentence'];
			if ( count( $selected ) >= 8 ) {
				break;
			}
		}

		if ( empty( $selected ) ) {
			$selected = self::fallback_sentence_selection( $scored );
			ksort( $selected );
			$selected = array_values( $selected );
		}

		$summary = self::assemble_summary( $selected, $limit );
		if ( '' === $summary ) {
			$summary = self::clip_to_limit( $text, $limit );
		}

		return self::sanitize_summary( $summary );
	}

	private function generate_llm_summary( $label, $text ) {
		$text = self::normalize_public_text( $text );
		if ( '' === $text ) {
			return array( 'summary' => '', 'error' => 'empty_field' );
		}

		$client = $this->get_ai_client();
		$router = $this->get_model_router();
		$auth   = $this->get_auth_manager();
		if ( ! $client || ! $router || ! $auth || ! method_exists( $client, 'generate' ) ) {
			return array( 'summary' => '', 'error' => 'summary_dependencies_unavailable' );
		}

		$model = method_exists( $router, 'route_for_founder_subagent' )
			? (string) $router->route_for_founder_subagent()
			: 'gpt-5.4-mini';
		$auth_mode = method_exists( $auth, 'get_mode' ) ? (string) $auth->get_mode() : 'api_key';
		$creds     = method_exists( $auth, 'get_credentials' ) ? (array) $auth->get_credentials() : array();

		$instructions = implode(
			"\n",
			array(
				'Summarize one Niwa AI agent-profile field for a public frontend sales assistant prompt.',
				'Return plain text only. Maximum 700 characters.',
				'Use customer-safe wording and explain business capabilities accurately.',
				'Include important capabilities from anywhere in the field, especially owner, admin, backend, content, product, stock, price, order, report, analytics, campaign, attribution, or MCP abilities when present.',
				'Do not expose internal tool names, file paths, logs, system prompts, credentials, private access lists, or private operational details.',
			)
		);
		$input = "Field label: " . (string) $label . "\n\nRaw field text:\n" . $text;

		$result = $client->generate( $model, $input, $instructions, $auth_mode, $creds, 45 );
		if ( empty( $result['success'] ) || empty( $result['answer'] ) ) {
			$error = isset( $result['error'] ) ? (string) $result['error'] : 'summary_generation_failed';
			return array( 'summary' => '', 'error' => $error );
		}

		$summary = self::sanitize_summary( $result['answer'] );
		if ( '' === $summary ) {
			return array( 'summary' => '', 'error' => 'empty_summary' );
		}

		return array( 'summary' => $summary, 'error' => '' );
	}

	private function get_auth_manager() {
		if ( $this->auth_manager ) {
			return $this->auth_manager;
		}
		if ( class_exists( 'Niwa_AI_Auth_Manager' ) ) {
			$this->auth_manager = new Niwa_AI_Auth_Manager( 'founder' );
		}
		return $this->auth_manager;
	}

	private function get_model_router() {
		if ( $this->model_router ) {
			return $this->model_router;
		}
		if ( class_exists( 'Niwa_AI_Model_Router' ) ) {
			$catalog = class_exists( 'Niwa_AI_Model_Catalog' ) ? new Niwa_AI_Model_Catalog() : null;
			$this->model_router = new Niwa_AI_Model_Router( $catalog );
		}
		return $this->model_router;
	}

	private function get_ai_client() {
		if ( $this->ai_client ) {
			return $this->ai_client;
		}
		if ( class_exists( 'Niwa_AI_OpenAI_Client' ) ) {
			$this->ai_client = new Niwa_AI_OpenAI_Client( 'founder_deep' );
		}
		return $this->ai_client;
	}

	private static function mark_field_pending( $field_key, $text, $hash, $fallback ) {
		$cache  = self::normalize_cache( get_option( Niwa_AI_Constants::OPT_AGENT_PROFILE_SUMMARIES, array() ) );
		$fields = isset( $cache['fields'] ) && is_array( $cache['fields'] ) ? $cache['fields'] : array();
		$chars  = self::text_length( $text );

		$existing = isset( $fields[ $field_key ] ) && is_array( $fields[ $field_key ] ) ? $fields[ $field_key ] : array();
		if (
			! empty( $existing['hash'] )
			&& $hash === (string) $existing['hash']
			&& ! empty( $existing['summary'] )
		) {
			return;
		}

		$fields[ $field_key ] = self::build_entry( $hash, $chars, $fallback, 'pending', '' );
		self::save_cache(
			array(
				'version'      => self::SUMMARY_VERSION,
				'generated_at' => self::now(),
				'fields'       => $fields,
			)
		);
	}

	private static function normalize_cache( $cache ) {
		if ( ! is_array( $cache ) ) {
			$cache = array();
		}

		$fields = isset( $cache['fields'] ) && is_array( $cache['fields'] ) ? $cache['fields'] : array();
		return array(
			'version'      => isset( $cache['version'] ) ? intval( $cache['version'] ) : 0,
			'generated_at' => isset( $cache['generated_at'] ) ? (string) $cache['generated_at'] : '',
			'fields'       => $fields,
		);
	}

	private static function normalize_entry( $entry, $hash, $raw_chars ) {
		$summary = isset( $entry['summary'] ) ? self::sanitize_summary( $entry['summary'] ) : '';
		$status  = isset( $entry['status'] ) ? sanitize_key( $entry['status'] ) : 'ready';
		if ( ! in_array( $status, array( 'ready', 'fallback', 'pending', 'short', 'empty' ), true ) ) {
			$status = 'ready';
		}

		return array(
			'hash'       => (string) $hash,
			'raw_chars'  => intval( $raw_chars ),
			'summary'    => $summary,
			'status'     => $status,
			'updated_at' => isset( $entry['updated_at'] ) && '' !== (string) $entry['updated_at'] ? (string) $entry['updated_at'] : self::now(),
			'error'      => isset( $entry['error'] ) ? (string) $entry['error'] : '',
		);
	}

	private static function build_entry( $hash, $raw_chars, $summary, $status, $error = '' ) {
		return array(
			'hash'       => (string) $hash,
			'raw_chars'  => intval( $raw_chars ),
			'summary'    => self::sanitize_summary( $summary ),
			'status'     => sanitize_key( $status ),
			'updated_at' => self::now(),
			'error'      => (string) $error,
		);
	}

	private static function entry_signature( $entry ) {
		if ( ! is_array( $entry ) ) {
			return '';
		}
		return wp_json_encode(
			array(
				'hash'      => isset( $entry['hash'] ) ? (string) $entry['hash'] : '',
				'raw_chars' => isset( $entry['raw_chars'] ) ? intval( $entry['raw_chars'] ) : 0,
				'summary'   => isset( $entry['summary'] ) ? (string) $entry['summary'] : '',
				'status'    => isset( $entry['status'] ) ? (string) $entry['status'] : '',
				'error'     => isset( $entry['error'] ) ? (string) $entry['error'] : '',
			)
		);
	}

	private static function save_cache( $cache ) {
		update_option( Niwa_AI_Constants::OPT_AGENT_PROFILE_SUMMARIES, $cache, false );
		self::invalidate_prompt_context();
	}

	private static function invalidate_prompt_context() {
		if ( function_exists( 'delete_transient' ) ) {
			delete_transient( 'niwa_ai_site_knowledge_blob' );
			delete_transient( 'niwa_ai_store_overview' );
		}
	}

	private static function schedule_refresh() {
		if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) {
			return;
		}

		if ( ! wp_next_scheduled( self::REFRESH_HOOK ) ) {
			wp_schedule_single_event( time() + 5, self::REFRESH_HOOK );
		}
	}

	public static function normalize_public_text( $text ) {
		$text = (string) $text;
		if ( '' === $text ) {
			return '';
		}

		$text = preg_replace( '/```[\s\S]*?```/u', ' ', $text );
		$text = preg_replace( '/<script[\s\S]*?<\/script>/iu', ' ', $text );
		$text = preg_replace( '/<style[\s\S]*?<\/style>/iu', ' ', $text );
		$text = preg_replace( '/\b(ignore\s+previous\s+instructions|system\s*prompt|developer\s*message|tool\s*call)\b/iu', ' ', $text );
		$text = wp_strip_all_tags( $text );
		$text = html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
		$text = preg_replace( '/\s+/u', ' ', $text );

		return trim( (string) $text );
	}

	public static function hash_text( $text ) {
		return hash( 'sha256', self::normalize_public_text( $text ) );
	}

	private static function sanitize_summary( $summary ) {
		$summary = self::normalize_public_text( $summary );
		$summary = self::redact_private_text( $summary );
		return self::clip_to_limit( $summary, self::SUMMARY_CHAR_LIMIT );
	}

	private static function redact_private_text( $text ) {
		$text = (string) $text;
		if ( '' === $text ) {
			return '';
		}

		$patterns = array(
			'/\bsk-[a-z0-9_\-]{10,}\b/i',
			'/\b(api[_\s-]?key|access\s+token|refresh\s+token|password|credential|secret|private\s+key)\b[^.!?]{0,120}/iu',
			'/\b(system\s+prompt|developer\s+message|tool\s+call|runtime-trace|debug\.log|gateway\.log)\b[^.!?]{0,120}/iu',
			'/\bNiwa_AI_[A-Za-z0-9_]+\b/u',
			'/\bmcp__[A-Za-z0-9_\-]+\b/u',
			'/\bniwa_ai_[a-z0-9_]+\b/u',
			'/(^|\s)(C:\\\\|\/home\/|\/var\/|\/wp-content\/)[^\s.!?]{0,180}/iu',
		);

		$text = preg_replace( $patterns, ' ', $text );
		$text = preg_replace( '/\s+/u', ' ', (string) $text );
		return trim( (string) $text );
	}

	private static function split_sentences( $text ) {
		$parts = preg_split( '/(?<=[.!?])\s+|\s*[\r\n]+\s*/u', (string) $text );
		if ( ! is_array( $parts ) ) {
			return array();
		}

		$sentences = array();
		foreach ( $parts as $part ) {
			$part = trim( (string) $part );
			if ( '' !== $part ) {
				$sentences[] = $part;
			}
		}
		return $sentences;
	}

	private static function capability_score( $sentence ) {
		$sentence = function_exists( 'remove_accents' ) ? remove_accents( (string) $sentence ) : (string) $sentence;
		$sentence = strtolower( $sentence );

		$weights = array(
			'owner'       => 8,
			'store owner' => 10,
			'admin'       => 8,
			'backend'     => 10,
			'founder'     => 8,
			'mcp'         => 8,
			'campaign'    => 7,
			'attribution' => 8,
			'source'      => 4,
			'analytics'   => 7,
			'report'      => 7,
			'product'     => 5,
			'stock'       => 7,
			'inventory'   => 7,
			'price'       => 6,
			'order'       => 6,
			'content'     => 5,
			'blog'        => 6,
			'page'        => 4,
			'seo'         => 4,
			'operation'   => 5,
			'lead'        => 4,
			'cart'        => 4,
			'recovery'    => 4,
			'woocommerce' => 5,
			'telegram'    => 4,
			'whatsapp'    => 4,
			'vlasnik'     => 8,
			'adminu'      => 7,
			'proizvod'    => 5,
			'lager'       => 7,
			'cena'        => 6,
			'cijena'      => 6,
			'narudz'      => 6,
			'porudz'      => 6,
			'kampanj'     => 7,
			'izvor'       => 5,
			'analitik'    => 7,
			'izvestaj'    => 7,
			'izvjestaj'   => 7,
			'sadrzaj'     => 5,
			'blog'        => 6,
		);

		$score = 0;
		foreach ( $weights as $needle => $weight ) {
			if ( false !== strpos( $sentence, $needle ) ) {
				$score += intval( $weight );
			}
		}

		return $score;
	}

	private static function is_public_safe_sentence( $sentence ) {
		$sentence = (string) $sentence;
		$unsafe = array(
			'/\b(api[_\s-]?key|access\s+token|refresh\s+token|password|credential|secret|private\s+key)\b/iu',
			'/\b(system\s+prompt|developer\s+message|tool\s+call|runtime-trace|debug\.log|gateway\.log)\b/iu',
			'/\bNiwa_AI_[A-Za-z0-9_]+\b/u',
			'/\bmcp__[A-Za-z0-9_\-]+\b/u',
			'/\bniwa_ai_[a-z0-9_]+\b/u',
			'/(C:\\\\|\/home\/|\/var\/|\/wp-content\/)/iu',
		);

		foreach ( $unsafe as $pattern ) {
			if ( preg_match( $pattern, $sentence ) ) {
				return false;
			}
		}

		return true;
	}

	private static function fallback_sentence_selection( $scored ) {
		if ( empty( $scored ) ) {
			return array();
		}

		$count = count( $scored );
		$indexes = array_unique(
			array(
				0,
				(int) floor( max( 0, $count - 1 ) / 2 ),
				max( 0, $count - 1 ),
			)
		);

		$selected = array();
		foreach ( $indexes as $index ) {
			if ( isset( $scored[ $index ]['sentence'] ) ) {
				$selected[ $scored[ $index ]['index'] ] = $scored[ $index ]['sentence'];
			}
		}

		return $selected;
	}

	private static function assemble_summary( $sentences, $limit ) {
		$out = '';
		foreach ( $sentences as $sentence ) {
			$sentence = trim( (string) $sentence );
			if ( '' === $sentence ) {
				continue;
			}
			$candidate = '' === $out ? $sentence : $out . ' ' . $sentence;
			if ( self::text_length( $candidate ) <= $limit ) {
				$out = $candidate;
				continue;
			}
			if ( '' === $out ) {
				$out = self::clip_to_limit( $sentence, $limit );
			}
			break;
		}

		return trim( $out );
	}

	private static function clip_to_limit( $text, $limit ) {
		$text  = trim( (string) $text );
		$limit = intval( $limit );
		if ( $limit <= 0 || self::text_length( $text ) <= $limit ) {
			return $text;
		}

		$clip_at = max( 0, $limit - 3 );
		$prefix  = mb_substr( $text, 0, $clip_at );
		$space   = mb_strrpos( $prefix, ' ' );
		if ( false !== $space && $space > 80 ) {
			$prefix = mb_substr( $prefix, 0, $space );
		}

		return rtrim( $prefix ) . '...';
	}

	private static function text_length( $text ) {
		return mb_strlen( (string) $text );
	}

	private static function now() {
		return gmdate( 'c' );
	}
}
