<?php
/**
 * Draft generation from approved plan service.
 *
 * @package SmartyPress_AI_Engine
 */

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

/**
 * Generates final draft content from legacy or dynamic plans.
 */
class SmartyPress_AI_Content_Generator_Plan_Draft_Generation_Service {

	/**
	 * Plan normalizer.
	 *
	 * @var SmartyPress_AI_Content_Generator_Plan_Normalizer
	 */
	private $normalizer;

	/**
	 * Summary builder.
	 *
	 * @var SmartyPress_AI_Content_Generator_Plan_Summary_Builder
	 */
	private $summary_builder;

	/**
	 * Draft parser.
	 *
	 * @var SmartyPress_AI_Content_Generator_Draft_Parser
	 */
	private $draft_parser;

	/**
	 * Constructor.
	 *
	 * @param SmartyPress_AI_Content_Generator_Plan_Normalizer      $normalizer Plan normalizer.
	 * @param SmartyPress_AI_Content_Generator_Plan_Summary_Builder $summary_builder Summary builder.
	 * @param SmartyPress_AI_Content_Generator_Draft_Parser         $draft_parser Draft parser.
	 */
	public function __construct( $normalizer, $summary_builder, $draft_parser ) {
		$this->normalizer      = $normalizer;
		$this->summary_builder = $summary_builder;
		$this->draft_parser    = $draft_parser;
	}

	/**
	 * Generate final post content from an approved plan.
	 *
	 * @param object   $provider Provider instance.
	 * @param string   $provider_name Provider name.
	 * @param array    $plan Approved plan.
	 * @param array    $options Generation options.
	 * @param string   $language Language.
	 * @param float    $default_temperature Default temperature.
	 * @param int      $default_max_tokens Default token limit.
	 * @param callable $cache_buster Cache buster callback.
	 * @param callable $log_request Draft logging callback.
	 * @return array
	 */
	public function generate_post_from_plan( $provider, $provider_name, $plan, $options, $language, $default_temperature, $default_max_tokens, $cache_buster, $log_request ) {
		$plan                = $this->normalizer->normalize_post_plan( $plan );
		$topic               = $options['topic'] ?? '';
		$temperature         = $options['temperature'] ?? $default_temperature;
		$max_tokens          = absint( $options['max_tokens'] ?? $default_max_tokens );
		$max_tokens          = max( 8000, $max_tokens );
		$keywords            = $this->collect_plan_keywords( $plan );
		$is_dynamic_plan     = $this->normalizer->is_dynamic_post_plan( $plan );
		$dynamic_summary     = $is_dynamic_plan ? $this->summary_builder->build_dynamic_plan_summary( $plan ) : '';
		$planned_title       = $is_dynamic_plan ? $this->summary_builder->find_dynamic_plan_title( $plan ) : ( $plan['title']['value'] ?? '' );
		$allow_image_markers = ! empty( $options['generate_image_markers'] );
		$is_product          = 'product' === sanitize_key( $options['post_type'] ?? '' );

		$system_prompt = $this->get_system_prompt( $language, $is_product );

		$user_prompt = $is_dynamic_plan
			? "APPROVED_DYNAMIC_PLAN:\n" . $dynamic_summary
			: "APPROVED_PLAN:\n" . wp_json_encode( $plan, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
		$user_prompt .= "\n\nTOPIC:\n" . $topic;

		if ( ! empty( $keywords ) ) {
			$user_prompt .= "\n\nKEYWORDS TO INCLUDE NATURALLY:\n- " . implode( "\n- ", $keywords );
		}

		$user_prompt .= $this->get_quality_rules_prompt( $is_product );

		if ( $allow_image_markers ) {
			$user_prompt .= "- Add 2-4 contextual image_placeholder blocks in natural positions where an image would help the reader. Each placeholder text must describe the exact image context. Each image_placeholder must include a default style selected from realistic_photo, editorial_illustration, light_infographic, product_shot, concept_art. Choose realistic_photo for real-world scenes, editorial_illustration for abstract or technology topics, light_infographic for concise explanatory visuals, product_shot for products or objects, and concept_art for atmospheric or imaginative topics.\n";
		}

		$user_prompt .= "\n\n" . $this->get_plan_source_instruction( $is_dynamic_plan, $is_product );
		$user_prompt .= "\n\n" . ( $is_product ? $this->get_product_blocks_prompt( $allow_image_markers ) : $this->get_article_blocks_prompt( $allow_image_markers ) );

		$params = array(
			'system_prompt'   => call_user_func( $cache_buster, $system_prompt ),
			'temperature'     => $temperature,
			'max_tokens'      => $max_tokens,
			'request_timeout' => 180,
			'response_format' => array(
				'type' => 'json_object',
			),
		);

		if ( ! empty( $options['model'] ) ) {
			$params['model'] = $options['model'];
		}

		call_user_func(
			$log_request,
			$provider_name,
			$options,
			$topic,
			$plan,
			$is_dynamic_plan ? 'dynamic' : 'legacy',
			$dynamic_summary,
			$user_prompt
		);

		$result = $provider->generate_content( $user_prompt, $params );

		if ( empty( $result['success'] ) ) {
			return $result;
		}

		$raw_content = $result['content'] ?? '';
		$parts       = $this->draft_parser->parse_generated_post_parts( $raw_content );

		if ( empty( $parts['title'] ) && ! empty( $planned_title ) ) {
			$parts['title'] = $planned_title;
		}

		if ( ! empty( $parts['content_blocks'] ) && is_array( $parts['content_blocks'] ) && empty( $parts['content_blocks']['title'] ) ) {
			$parts['content_blocks']['title'] = $parts['title'];
		}

		if ( empty( $parts['content'] ) && empty( $parts['content_blocks'] ) ) {
			$parts['content'] = $this->draft_parser->extract_unmarked_generated_content( $raw_content, $parts['title'] ?? '' );
		}

		if ( empty( $parts['title'] ) || ( empty( $parts['content'] ) && empty( $parts['content_blocks'] ) ) ) {
			return array(
				'success'    => false,
				'error'      => __( 'Failed to parse the generated draft from the plan.', 'smartypress-ai-engine' ),
				'error_code' => 'draft_parse_failed',
			);
		}

		return array(
			'success'        => true,
			'title'          => sanitize_text_field( $parts['title'] ),
			'content'        => SmartyPress_AI_Prompt_Processor::clean_generated_content( $parts['content'], $parts['title'] ),
			'excerpt'        => sanitize_textarea_field( $parts['excerpt'] ?? '' ),
			'seo_keywords'   => ! empty( $parts['seo_keywords'] ) ? $this->normalizer->sanitize_string_list( $parts['seo_keywords'] ) : $keywords,
			'seo_metadata'   => is_array( $parts['seo_metadata'] ?? null ) ? $parts['seo_metadata'] : array(),
			'content_format' => sanitize_key( $parts['content_format'] ?? 'legacy_markdown' ),
			'content_blocks' => is_array( $parts['content_blocks'] ?? null ) ? $parts['content_blocks'] : array(),
		);
	}

	/**
	 * Generate and create a draft post from an approved plan.
	 *
	 * @param callable $draft_generator Draft generator callback.
	 * @param callable $create_post Create post callback.
	 * @param array    $plan Approved plan.
	 * @param string   $provider_name Provider name.
	 * @param array    $options Generation options.
	 * @return array
	 */
	public function generate_draft_from_plan( $draft_generator, $create_post, $plan, $provider_name, $options ) {
		$draft = call_user_func( $draft_generator, $plan, $provider_name, $options );

		if ( empty( $draft['success'] ) ) {
			return $draft;
		}

		$create_result = call_user_func(
			$create_post,
			array(
				'post_title'          => $draft['title'],
				'post_content'        => $draft['content'],
				'post_excerpt'        => $draft['excerpt'],
				'post_status'         => 'draft',
				'post_type'           => $options['post_type'] ?? 'post',
				'content_format'      => $draft['content_format'] ?? 'legacy_markdown',
				'post_content_blocks' => $draft['content_blocks'] ?? array(),
				'seo_keywords'        => $draft['seo_keywords'] ?? array(),
				'seo_metadata'        => $draft['seo_metadata'] ?? array(),
			)
		);

		if ( empty( $create_result['success'] ) ) {
			return $create_result;
		}

		$post_id = absint( $create_result['post_id'] );

		update_post_meta( $post_id, 'smartypress_ai_generation_plan', $this->normalizer->normalize_post_plan( $plan ) );
		update_post_meta( $post_id, 'smartypress_ai_seo_keywords', $draft['seo_keywords'] );
		update_post_meta( $post_id, 'smartypress_ai_generation_topic', sanitize_textarea_field( $options['topic'] ?? '' ) );
		update_post_meta( $post_id, 'smartypress_ai_generation_provider', sanitize_key( $provider_name ) );
		update_post_meta( $post_id, 'smartypress_ai_generation_model', sanitize_text_field( $options['model'] ?? '' ) );
		update_post_meta( $post_id, 'smartypress_ai_content_format', sanitize_key( $draft['content_format'] ?? 'legacy_markdown' ) );
		update_post_meta( $post_id, '_smartypress_ai_generation_language', sanitize_text_field( $options['language'] ?? '' ) );

		return array_merge(
			$create_result,
			array(
				'title'        => $draft['title'],
				'content'      => $draft['content'],
				'excerpt'      => $draft['excerpt'],
				'seo_keywords' => $draft['seo_keywords'],
				'seo_metadata' => $create_result['seo_metadata'] ?? array(),
			)
		);
	}

	/**
	 * Get strict prompt for structured article blocks.
	 *
	 * @return string
	 */
	private function get_article_blocks_prompt( $allow_image_markers = false ) {
		if ( $allow_image_markers ) {
			return __( 'Return a single JSON object with this exact top-level structure: {"title":"...","excerpt":"...","seo_keywords":["..."],"seo_metadata":{"seo_title":"...","meta_description":"...","slug":"...","focus_keywords":["..."]},"content":{"lead":"...","toc":true,"blocks":[...]}}. Supported blocks only: heading, paragraph, list, callout, table, image_placeholder, separator. Heading blocks require {"type":"heading","level":2|3|4,"text":"...","anchor":"..."}. Paragraph blocks require {"type":"paragraph","text":"..."}. List blocks require {"type":"list","ordered":false,"items":["..."]}. Callout blocks require {"type":"callout","style":"highlight|info|warning","title":"...","content":"..."}. Table blocks require {"type":"table","headers":["..."],"rows":[["..."]]}. Image placeholder blocks require {"type":"image_placeholder","text":"...","style":"realistic_photo|editorial_illustration|light_infographic|product_shot|concept_art"}. Use a strong lead, server-generated TOC from H2 sections, clear H2/H3 hierarchy, useful lists, tables only when they improve comprehension, callouts only for important notes, and 2-4 image placeholders where a visual example would help. Choose the image_placeholder style that best matches the image context. Do not include CTA blocks, arbitrary HTML, CSS, Markdown headings, or unsupported block types.', 'smartypress-ai-engine' );
		}

		return __( 'Return a single JSON object with this exact top-level structure: {"title":"...","excerpt":"...","seo_keywords":["..."],"seo_metadata":{"seo_title":"...","meta_description":"...","slug":"...","focus_keywords":["..."]},"content":{"lead":"...","toc":true,"blocks":[...]}}. Supported blocks only: heading, paragraph, list, callout, table, separator. Heading blocks require {"type":"heading","level":2|3|4,"text":"...","anchor":"..."}. Paragraph blocks require {"type":"paragraph","text":"..."}. List blocks require {"type":"list","ordered":false,"items":["..."]}. Callout blocks require {"type":"callout","style":"highlight|info|warning","title":"...","content":"..."}. Table blocks require {"type":"table","headers":["..."],"rows":[["..."]]}. Use a strong lead, server-generated TOC from H2 sections, clear H2/H3 hierarchy, useful lists, tables only when they improve comprehension, and callouts only for important notes. Do not include image_placeholder blocks, CTA blocks, arbitrary HTML, CSS, Markdown headings, or unsupported block types.', 'smartypress-ai-engine' );
	}

	/**
	 * Get system prompt for article or product draft generation.
	 *
	 * @param string $language Language name.
	 * @param bool   $is_product Whether the target post type is product.
	 * @return string
	 */
	private function get_system_prompt( $language, $is_product ) {
		if ( $is_product ) {
			return sprintf(
				/* translators: %s: Language name. */
				__( 'You are a professional WooCommerce ecommerce copywriter and product content designer. Generate the final WooCommerce product draft from the approved plan in %s. Return only valid JSON. Do not return Markdown, HTML, comments, or explanatory text.', 'smartypress-ai-engine' ),
				$language
			);
		}

		return sprintf(
			/* translators: %s: Language name. */
			__( 'You are a professional content writer and content designer. Generate the final WordPress draft from the approved plan in %s. Return only valid JSON. Do not return Markdown, HTML, comments, or explanatory text.', 'smartypress-ai-engine' ),
			$language
		);
	}

	/**
	 * Get quality rules for article or product draft generation.
	 *
	 * @param bool $is_product Whether the target post type is product.
	 * @return string
	 */
	private function get_quality_rules_prompt( $is_product ) {
		if ( $is_product ) {
			return "\n\nPRODUCT CONTENT AND SEO RULES:\n"
				. "- Generate a WooCommerce product draft, not a blog article.\n"
				. "- The JSON title field must be the product name, not a marketing headline or SEO article title.\n"
				. "- Keep the JSON title focused on product identity: brand, line, model, product type, and explicitly provided identifying attributes such as size, variant, color, material, volume, capacity, bundle, model number, generation, year, or edition.\n"
				. "- Do not append promotional phrases such as \"High-Performance Laptop for Professionals\", \"Best Choice\", \"Premium Solution\", \"for Professionals\", \"for Home\", or similar benefit-led endings to the JSON title unless they are part of the official product name.\n"
				. "- Put benefits, buyer intent, and SEO-style wording into the excerpt, content, SEO title, and meta description instead of the WooCommerce product title.\n"
				. "- The JSON excerpt field must be the WooCommerce short description shown near the Add to Cart area.\n"
				. "- The JSON content field must be the full product description.\n"
				. "- Use product-oriented sections such as benefits, features, specifications, use cases, compatibility, care notes, trust signals, and FAQ when relevant.\n"
				. "- Return SEO title, meta description, slug, and focus keywords in the JSON object.\n"
				. "- Use the primary keyword naturally in the product title, short description, full description, SEO title, meta description, and image ALT guidance when it fits.\n"
				. "- Do not invent price, stock quantity, SKU, dimensions, materials, compatibility, warranty, shipping terms, or certifications unless the approved plan or topic explicitly provides them.\n"
				. "- If important product facts are unknown, mark them as editor verification notes inside the product description instead of inventing facts.\n"
				. "- Do not add article-style conclusions, blog CTAs, or unrelated educational sections.\n";
		}

		return "\n\nSEO QUALITY RULES:\n"
			. "- Use the primary keyword naturally in the first 10% of the article.\n"
			. "- Use the primary keyword in at least one H2/H3 heading when it reads naturally.\n"
			. "- Keep keyword density natural: include the primary keyword several times across the article, but avoid stuffing.\n"
			. "- Generate at least 600 words unless the approved plan explicitly requires a shorter article.\n"
			. "- Return SEO title, meta description, slug, and focus keywords in the JSON object.\n"
			. "- Do not add a CTA block or sales call-to-action at the end of the article.\n";
	}

	/**
	 * Get source-plan instruction for article or product draft generation.
	 *
	 * @param bool $is_dynamic_plan Whether plan is dynamic.
	 * @param bool $is_product      Whether the target post type is product.
	 * @return string
	 */
	private function get_plan_source_instruction( $is_dynamic_plan, $is_product ) {
		if ( $is_product ) {
			return $is_dynamic_plan
				? __( 'Use the approved dynamic product plan as the source of truth. Follow its labels, notes, lists, repeaters, and product draft instruction naturally. If the plan includes product title, benefits, features, specifications, short description, SEO, or FAQ fields, use them as the base structure. Keep the final WooCommerce product title as a product name, not a marketing headline. Move benefit-led or SEO wording to the short description, full description, SEO title, and meta description. Adapt carefully for ecommerce clarity and avoid keyword stuffing.', 'smartypress-ai-engine' )
				: __( 'Use title.value as the base product title and use the approved product sections as the product description structure. Preserve title.value as a product name: do not expand it into a marketing headline, SEO article title, or benefit-led slogan. Use the keywords naturally in the short description, full description, SEO metadata, and headings where relevant. Do not create a separate keyword list in the product description unless the plan explicitly asks for it. Avoid keyword stuffing.', 'smartypress-ai-engine' );
		}

		return $is_dynamic_plan
			? __( 'Use the approved dynamic plan as the source of truth. Follow its labels, notes, lists, repeaters, and draft instruction naturally. If the plan includes title or section fields, use them as the base structure. Adapt carefully for readability and avoid keyword stuffing.', 'smartypress-ai-engine' )
			: __( 'Use title.value as the base title and use the approved section headings as the article structure. Use the keywords naturally in the final title, headings, paragraphs, and subheadings where relevant. If title.value does not include important keywords, adapt the final title carefully while preserving readability and meaning. Do not create a separate keyword list in the article unless the plan explicitly asks for it. Avoid keyword stuffing.', 'smartypress-ai-engine' );
	}

	/**
	 * Get strict prompt for structured WooCommerce product blocks.
	 *
	 * @param bool $allow_image_markers Whether image placeholders are allowed.
	 * @return string
	 */
	private function get_product_blocks_prompt( $allow_image_markers = false ) {
		if ( $allow_image_markers ) {
			return __( 'Return a single JSON object with this exact top-level structure: {"title":"...","excerpt":"...","seo_keywords":["..."],"seo_metadata":{"seo_title":"...","meta_description":"...","slug":"...","focus_keywords":["..."]},"content":{"lead":"...","toc":false,"blocks":[...]}}. The excerpt must be a concise WooCommerce short description. The content blocks must form the full product description. Supported blocks only: heading, paragraph, list, callout, table, image_placeholder, separator. Use product-focused blocks for benefits, features, specifications, use cases, compatibility, care notes, trust signals, and FAQ when useful. Image placeholder blocks require {"type":"image_placeholder","text":"...","style":"product_shot|realistic_photo|editorial_illustration|light_infographic|concept_art"} and should describe product images or usage visuals. Do not include price, SKU, stock quantity, shipping claims, warranty claims, arbitrary HTML, CSS, Markdown headings, or unsupported block types.', 'smartypress-ai-engine' );
		}

		return __( 'Return a single JSON object with this exact top-level structure: {"title":"...","excerpt":"...","seo_keywords":["..."],"seo_metadata":{"seo_title":"...","meta_description":"...","slug":"...","focus_keywords":["..."]},"content":{"lead":"...","toc":false,"blocks":[...]}}. The excerpt must be a concise WooCommerce short description. The content blocks must form the full product description. Supported blocks only: heading, paragraph, list, callout, table, separator. Use product-focused blocks for benefits, features, specifications, use cases, compatibility, care notes, trust signals, and FAQ when useful. Do not include image_placeholder blocks, price, SKU, stock quantity, shipping claims, warranty claims, arbitrary HTML, CSS, Markdown headings, or unsupported block types.', 'smartypress-ai-engine' );
	}

	/**
	 * Collect all keywords from a normalized plan.
	 *
	 * @param array $plan Normalized plan.
	 * @return array
	 */
	private function collect_plan_keywords( $plan ) {
		if ( $this->normalizer->is_dynamic_post_plan( $plan ) ) {
			return $this->summary_builder->collect_dynamic_plan_keywords( $plan['data'] );
		}

		$keywords = array_merge(
			$plan['content']['keywords'] ?? array(),
			$plan['seo']['keywords'] ?? array()
		);

		foreach ( $plan['content']['sections'] ?? array() as $section ) {
			$keywords = array_merge( $keywords, $section['keywords'] ?? array() );
		}

		return $this->normalizer->sanitize_string_list( $keywords );
	}
}
