<?php
/**
 * Standard Generator
 *
 * @package AIPT\Api\BulkGenerator\Standard
 * @since 2.5.0
 */

namespace AIPT\Api\BulkGenerator\Standard;

use AIPT\Api\BulkGenerator\Standard\Core\StandardProductDataProvider;
use AIPT\Api\BulkGenerator\Standard\Core\StandardPromptFactory;
use AIPT\Api\BulkGenerator\Standard\Core\PlainTextSanitizer;
use AIPT\Api\BulkGenerator\Common\AIClientFactory;
use AIPT\Api\BulkGenerator\Common\ProductDataFormatter;
use AIPT\Core\ErrorHandler;
use AIPT\Utils\ImagePreserver;

class StandardGenerator {

	private $client;
	private array $options;

	public function __construct( array $options = array() ) {
		$this->options = $options;
		$provider = $options['provider'] ?? get_option( 'aipt_api_provider', 'openai' );
		$this->client = AIClientFactory::create( $provider, array(
			'model' => $options['model'] ?? null,
		) );
	}

	/**
	 * @param int[] $product_ids Product IDs to generate content for.
	 * @param array $options     Generation options.
	 * @return array Generation results
	 */
	public function bulkGenerate( array $product_ids, array $options ): array {
		$results = array();

		$products_data = StandardProductDataProvider::bulkGet( $product_ids );

		foreach ( $product_ids as $product_id ) {
			if ( ! isset( $products_data[ $product_id ] ) ) {
				$results[] = array(
					'product_id' => $product_id,
					'error'      => 'Product not found',
				);
				continue;
			}

			$product_data = $products_data[ $product_id ];

			if ( ! empty( $options['generateShortDescription'] ) ) {
				$results[] = $this->generateContent( $product_data, 'short', $options );
			}

			if ( ! empty( $options['generateDescription'] ) ) {
				$results[] = $this->generateContent( $product_data, 'full', $options );
			}

			if ( ! empty( $options['generateTags'] ) ) {
				$results[] = $this->generateTags( $product_data, $options );
			}
		}

		return $results;
	}

	private function generateContent( array $product_data, string $type, array $options ): array {
		try {
			$image_map = array();

			if ( $this->shouldPreserveContentImages( $options ) && empty( $options['keepExistingDescription'] ) ) {
				$product = wc_get_product( $product_data['id'] );
				if ( $product ) {
					$field            = 'short' === $type ? 'short_description' : 'description';
					$existing_content = 'short' === $type ? $product->get_short_description() : $product->get_description();
					$masked           = ImagePreserver::mask( (string) $existing_content );
					$product_data[ $field ] = $masked['content'];
					$image_map             = $masked['imageMap'];
				}
			}

			$prompts = StandardPromptFactory::build( $type, $product_data, $options );

			$content = $this->client->generate_description(
				$prompts['system_prompt'],
				$prompts['user_prompt'],
				$options['model'] ?? null
			);

			$placeholder_tokens = array();
			if ( ! empty( $image_map ) ) {
				foreach ( array_keys( $image_map ) as $index => $placeholder ) {
					$token = 'AIPTIMGPLACEHOLDER' . $index;
					$placeholder_tokens[ $token ] = $placeholder;
					$content = str_replace( $placeholder, $token, $content );
				}
			}

			$content = PlainTextSanitizer::sanitize( $content );
			if ( ! empty( $placeholder_tokens ) ) {
				$content = str_replace( array_keys( $placeholder_tokens ), array_values( $placeholder_tokens ), $content );
			}

			if ( ! empty( $image_map ) ) {
				$unmasked = ImagePreserver::unmask( $content, $image_map );
				$content  = $unmasked['content'];
			}

			if (
				! empty( $options['keepExistingDescription'] )
				&& empty( $options['improveExistingDescription'] )
			) {
				$product = wc_get_product( $product_data['id'] );
				if ( $product ) {
					$content = ProductDataFormatter::appendExistingDescription(
						$product,
						$content,
						$type,
						$options
					);
				}
			}

			return array(
				'product_id'   => $product_data['id'],
				'product_name' => $product_data['name'],
				'type'         => $type,
				'text'         => $content,
			);

		} catch ( \Exception $e ) {
			return array(
				'product_id'   => $product_data['id'],
				'product_name' => $product_data['name'],
				'type'         => $type,
				'api_error'    => $this->formatError( $e->getMessage() ),
			);
		}
	}

	private function shouldPreserveContentImages( array $options ): bool {
		if ( array_key_exists( 'preserve_content_images', $options ) ) {
			return (bool) $options['preserve_content_images'];
		}

		return (bool) get_option( 'aipt_preserve_content_images', true );
	}

	private function generateTags( array $product_data, array $options ): array {
		try {
			$use_additional = ! empty( $options['keepExistingDescription'] ) && ! empty( $product_data['tags'] );

			if ( $use_additional ) {
				$prompts = StandardPromptFactory::buildAdditionalTagsPrompt( $product_data, $options );
			} else {
				$prompts = StandardPromptFactory::build( 'tags', $product_data, $options );
			}

			$tags = $this->client->generate_description(
				$prompts['system_prompt'],
				$prompts['user_prompt'],
				$options['model'] ?? null
			);

			$tags = PlainTextSanitizer::sanitizeTags( $tags );

			return array(
				'product_id'   => $product_data['id'],
				'product_name' => $product_data['name'],
				'type'         => 'tags',
				'text'         => $tags,
			);

		} catch ( \Exception $e ) {
			return array(
				'product_id'   => $product_data['id'],
				'product_name' => $product_data['name'],
				'type'         => 'tags',
				'api_error'    => $this->formatError( $e->getMessage() ),
			);
		}
	}

	private function formatError( string $message ): string {
		$provider = $this->options['provider'] ?? get_option( 'aipt_api_provider', 'openai' );
		return ErrorHandler::format_api_error_message( ucfirst( $provider ), $message );
	}

	/**
	 * @param array $items Items to apply: [['product_id' => int, 'type' => string, 'text' => string], ...]
	 * @param array $options Application options.
	 * @return array Application results
	 */
	public function applyDescriptions( array $items, array $options = array() ): array {
		$applied_count = 0;
		$results = array();
		$errors = array();

		foreach ( $items as $item ) {
			if ( ! is_array( $item ) || empty( $item['product_id'] ) ) {
				continue;
			}

			$product_id = absint( $item['product_id'] );
			$type = sanitize_text_field( $item['type'] ?? '' );
			$text = $item['text'] ?? '';

			if ( empty( $product_id ) || empty( $type ) || empty( $text ) ) {
				continue;
			}

			$product = wc_get_product( $product_id );
			if ( ! $product ) {
				$errors[] = "Product ID {$product_id} not found";
				$results[] = array(
					'product_id' => $product_id,
					'type'       => $type,
					'success'    => false,
					'error'      => 'Product not found',
				);
				continue;
			}

			try {
				$success = $this->applyToProduct( $product, $type, $text, $item, $options );
				if ( $success ) {
					$applied_count++;
					StandardProductDataProvider::clearCache( $product_id );
				}
				$results[] = array(
					'product_id' => $product_id,
					'type'       => $type,
					'success'    => $success,
				);
			} catch ( \Exception $e ) {
				$error_message = $e->getMessage();
				$errors[] = "Error updating product ID {$product_id}: {$error_message}";
				$results[] = array(
					'product_id' => $product_id,
					'type'       => $type,
					'success'    => false,
					'error'      => $error_message,
				);
			}
		}

		$message = 'Descriptions applied successfully';
		if ( ! empty( $errors ) ) {
			$message = $applied_count > 0
				? 'Some descriptions were applied successfully, but there were errors: ' . implode( ', ', $errors )
				: 'Failed to apply descriptions: ' . implode( ', ', $errors );
		}

		return array(
			'message'       => $message,
			'applied_count' => $applied_count,
			'results'       => $results,
			'has_errors'    => ! empty( $errors ),
		);
	}

	private function applyToProduct( \WC_Product $product, string $type, string $text, array $item, array $options ): bool {
		switch ( $type ) {
			case 'short':
				$product->set_short_description( $text );
				$product->save();
				return true;

			case 'full':
				$product->set_description( $text );
				$product->save();
				return true;

			case 'tags':
				$tag_array = array_map( 'trim', explode( ',', $text ) );
				$append = ! empty( $item['keepExistingDescription'] ) || ! empty( $options['keepExistingDescription'] );
				wp_set_object_terms( $product->get_id(), $tag_array, 'product_tag', $append );
				return true;

			default:
				return false;
		}
	}
}
