<?php

namespace AIPT\Api\TaxonomyGenerator;

use AIPT\Utils\PromptFormatter;

class TaxonomyPromptFactory {

	/**
	 * Build system and user prompt pair for taxonomy description generation.
	 *
	 * @param array $term_context Context built from taxonomy and sampled products.
	 * @param array $options      Prompt options (purpose, tone, language, length, customPurpose).
	 *
	 * @return array{system:string,user:string}
	 */
	public function buildPrompt( array $term_context, array $options ): array {
		$purpose = $this->resolvePurpose( $options );
		$tone    = $this->resolveOptionString( $options, 'tone', 'Professional' );
		$language = $this->resolveOptionString( $options, 'language', 'English' );
		$length   = $this->resolveOptionString( $options, 'length', 'medium' );

		$purpose_details        = $this->getPurposeDetails( $purpose, $options, $length );
		$product_context_block  = $this->buildProductContext( $term_context );
		$has_product_context    = ( '' !== trim( $product_context_block ) ) && false === strpos( $product_context_block, 'No sampled product context is available' );
		$term_data_block        = $this->buildTermDataBlock( $term_context );
		$task_context_modifier  = $has_product_context
			? 'Use term metadata together with product context to produce an accurate description.'
			: 'Product context is empty. Rely on taxonomy metadata and existing term signals only.';

		$system_prompt = PromptFormatter::cleanContent( $this->buildSystemPrompt( $tone, $language, $purpose, $purpose_details['rules'], $purpose_details['length_description'] ) );
		$user_prompt   = PromptFormatter::cleanContent(
			$term_data_block . "\n\n" .
			"<product_context>\n" . $product_context_block . "\n</product_context>\n\n" .
			"# Task\n" .
			$purpose_details['task'] . "\n" .
			$task_context_modifier
		);

		$additional_prompt = $this->resolveOptionString( $options, 'additionalPrompt', '' );
		if ( '' !== $additional_prompt ) {
			$clean_additional = PromptFormatter::cleanContent( $additional_prompt );
			if ( '' !== trim( $clean_additional ) ) {
				$user_prompt .= "\n\n<additional_instructions>\n" . $clean_additional . "\n</additional_instructions>";
			}
		}

		return array(
			'system' => $system_prompt,
			'user'   => $user_prompt,
		);
	}

	private function buildSystemPrompt( string $tone, string $language, string $purpose, string $purpose_rules, string $length_description ): string {
		return "# Identity
You are an expert e-commerce content writer specializing in product category descriptions for WooCommerce stores.

# Rules
- Output ONLY valid HTML using <p>, <ul>, <li>, <strong>, <em> tags
- NEVER use markdown (no **, no ##, no -, no *)
- NEVER invent products, prices, features, or claims not present in the provided context
- Use ONLY the information provided in <term_data> and <product_context>
- Match the specified tone: {$tone}
- Write in: {$language}
- Target length: {$length_description}
- Purpose: {$purpose}
- {$purpose_rules}

# Quality Gate
Before responding, verify:
1. Is the output valid HTML and free of markdown?
2. Does the result match the {$purpose} purpose?
3. Does the description avoid invented facts and unsupported claims?
4. Is the output within the requested length target?";
	}

	private function buildTermDataBlock( array $term_context ): string {
		$term_name             = $this->contextString( $term_context, 'term_name', 'Unknown Term' );
		$taxonomy_label        = $this->contextString( $term_context, 'taxonomy_label', $this->contextString( $term_context, 'taxonomy', 'product_cat' ) );
		$breadcrumb            = $this->contextString( $term_context, 'breadcrumb', 'N/A' );
		$existing_description  = $this->contextString( $term_context, 'existing_description', 'None' );
		$child_terms           = $this->contextArrayString( $term_context, 'child_terms', 'None' );
		$product_count         = isset( $term_context['product_count'] ) ? absint( $term_context['product_count'] ) : 0;

		return "<term_data>
Term: {$term_name}
Taxonomy: {$taxonomy_label}
Breadcrumb: {$breadcrumb}
Existing Description: {$existing_description}
Child Categories: {$child_terms}
Product Count: {$product_count}
</term_data>";
	}

	private function buildProductContext( array $term_context ): string {
		if ( empty( $term_context['product_summary'] ) || ! is_array( $term_context['product_summary'] ) ) {
			return 'No sampled product context is available for this term.';
		}

		$product_summary = $term_context['product_summary'];
		$lines           = array();

		if ( ! empty( $product_summary['names'] ) && is_array( $product_summary['names'] ) ) {
			$lines[] = 'Sample Product Names: ' . implode( ', ', array_map( 'sanitize_text_field', array_slice( $product_summary['names'], 0, 15 ) ) );
		}

		if ( ! empty( $product_summary['descriptions'] ) && is_array( $product_summary['descriptions'] ) ) {
			$description_samples = array();
			foreach ( array_slice( $product_summary['descriptions'], 0, 5 ) as $description ) {
				$description_samples[] = $this->trimWords( sanitize_text_field( wp_strip_all_tags( (string) $description ) ), 30 );
			}
			if ( ! empty( $description_samples ) ) {
				$lines[] = 'Product Description Signals: ' . implode( ' | ', $description_samples );
			}
		}

		if ( ! empty( $product_summary['price_range'] ) && is_array( $product_summary['price_range'] ) ) {
			$min = $product_summary['price_range']['min'] ?? null;
			$max = $product_summary['price_range']['max'] ?? null;
			if ( null !== $min && null !== $max ) {
				$lines[] = sprintf( 'Observed Price Range: %s - %s', (string) $min, (string) $max );
			} elseif ( null !== $max ) {
				$lines[] = sprintf( 'Observed Price Range: Up to %s', (string) $max );
			} elseif ( null !== $min ) {
				$lines[] = sprintf( 'Observed Price Range: From %s', (string) $min );
			}
		}

		if ( ! empty( $product_summary['attributes'] ) && is_array( $product_summary['attributes'] ) ) {
			$lines[] = 'Attribute Summary: ' . implode( '; ', array_map( 'sanitize_text_field', array_slice( $product_summary['attributes'], 0, 20 ) ) );
		}

		if ( empty( $lines ) ) {
			return 'No sampled product context is available for this term.';
		}

		return implode( "\n", $lines );
	}

	/**
	 * @return array{rules:string,task:string,length_description:string}
	 */
	private function getPurposeDetails( string $purpose, array $options, string $length ): array {
		$length_description = $this->resolveLengthDescription( $length, $purpose );

		$presets = array(
			'seo_description'   => array(
				'rules' => 'Prioritize discoverability with natural keyword integration and clear search intent coverage.',
				'task'  => 'Generate an SEO-focused taxonomy description that targets likely category keywords and search intent.',
			),
			'marketing_overview' => array(
				'rules' => 'Emphasize customer benefits, emotional value, and persuasive brand voice while staying factual.',
				'task'  => 'Generate a marketing overview for this taxonomy term with benefit-led messaging and compelling language.',
			),
			'technical_summary' => array(
				'rules' => 'Focus on concrete specifications, product types, and objective facts without hype.',
				'task'  => 'Generate a technical summary that gives shoppers a clear, factual overview of what this category includes.',
			),
			'brand_story'       => array(
				'rules' => 'Use narrative style connected to brand values, lifestyle fit, and category purpose without inventing claims.',
				'task'  => 'Generate a brand story style description that frames this taxonomy term in a lifestyle and values context.',
			),
			'custom'            => array(
				'rules' => 'Follow the custom purpose exactly while preserving HTML-only output and strict factual grounding.',
				'task'  => $this->resolveOptionString( $options, 'customPurpose', 'Generate a category description aligned with the custom purpose provided by the user.' ),
			),
		);

		if ( ! isset( $presets[ $purpose ] ) ) {
			$purpose = 'seo_description';
		}

		$selected = $presets[ $purpose ];

		if ( 'seo_description' === $purpose && '150-300 words' === $length_description ) {
			$selected['task'] .= ' Keep the output between 150 and 300 words.';
		}

		return array(
			'rules'              => $selected['rules'],
			'task'               => $selected['task'],
			'length_description' => $length_description,
		);
	}

	private function resolvePurpose( array $options ): string {
		$purpose = $this->resolveOptionString( $options, 'purpose', 'seo_description' );
		$allowed = array( 'seo_description', 'marketing_overview', 'technical_summary', 'brand_story', 'custom' );

		if ( ! in_array( $purpose, $allowed, true ) ) {
			return 'seo_description';
		}

		return $purpose;
	}

	private function resolveLengthDescription( string $length, string $purpose ): string {
		if ( 'seo_description' === $purpose && ( 'medium' === $length || '' === $length ) ) {
			return '150-300 words';
		}

		$map = array(
			'short'  => '80-140 words',
			'medium' => '150-300 words',
			'long'   => '300-450 words',
		);

		return $map[ $length ] ?? $length;
	}

	private function resolveOptionString( array $options, string $key, string $fallback ): string {
		if ( ! isset( $options[ $key ] ) || ! is_string( $options[ $key ] ) ) {
			return $fallback;
		}

		$value = trim( wp_strip_all_tags( $options[ $key ] ) );

		return '' === $value ? $fallback : $value;
	}

	private function contextString( array $context, string $key, string $fallback ): string {
		if ( ! isset( $context[ $key ] ) ) {
			return $fallback;
		}

		$value = is_scalar( $context[ $key ] ) ? (string) $context[ $key ] : '';

		$value = trim( wp_strip_all_tags( $value ) );

		return '' === $value ? $fallback : $value;
	}

	private function contextArrayString( array $context, string $key, string $fallback ): string {
		if ( empty( $context[ $key ] ) || ! is_array( $context[ $key ] ) ) {
			return $fallback;
		}

		$values = array_map(
			static function ( $item ): string {
				if ( ! is_scalar( $item ) ) {
					return '';
				}

				return trim( wp_strip_all_tags( (string) $item ) );
			},
			$context[ $key ]
		);

		$values = array_values( array_filter( $values ) );

		if ( empty( $values ) ) {
			return $fallback;
		}

		return implode( ', ', $values );
	}

	private function trimWords( string $text, int $word_limit ): string {
		$clean_text = trim( preg_replace( '/\s+/', ' ', $text ) ?? '' );

		if ( '' === $clean_text ) {
			return '';
		}

		$words = explode( ' ', $clean_text );
		if ( count( $words ) <= $word_limit ) {
			return $clean_text;
		}

		return implode( ' ', array_slice( $words, 0, $word_limit ) ) . '...';
	}
}
