<?php
/**
 * Dynamic plan generation service.
 *
 * @package SmartyPress_AI_Engine
 */

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

/**
 * Generates dynamic plan schema and fills it with concrete data.
 */
class SmartyPress_AI_Content_Generator_Plan_Generation_Service {

	/**
	 * Plan parser.
	 *
	 * @var SmartyPress_AI_Content_Generator_Plan_Parser
	 */
	private $parser;

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

	/**
	 * Constructor.
	 *
	 * @param SmartyPress_AI_Content_Generator_Plan_Parser     $parser Plan parser.
	 * @param SmartyPress_AI_Content_Generator_Plan_Normalizer $normalizer Plan normalizer.
	 */
	public function __construct( $parser, $normalizer ) {
		$this->parser     = $parser;
		$this->normalizer = $normalizer;
	}

	/**
	 * Generate a dynamic editable plan schema.
	 *
	 * @param object   $provider AI provider.
	 * @param string   $plan_prompt_template User-facing plan instruction.
	 * @param array    $plan_context Runtime context.
	 * @param array    $options Request options.
	 * @param string   $site_language Site language fallback.
	 * @param callable $cache_buster Cache buster callback.
	 * @return array
	 */
	public function generate_post_plan_schema( $provider, $plan_prompt_template, $plan_context, $options, $site_language, $cache_buster ) {
		$language = $options['language'] ?? $site_language;
		$expert_mode = ! empty( $options['plan_expert_mode'] ) || ! empty( $plan_context['plan_expert_mode'] );

		$system_prompt = sprintf(
			/* translators: %s: Language name. */
			__( 'You are an editorial planning form architect. Create a flexible editable form schema in %s for a content plan. Return ONLY valid JSON wrapped between ===PLAN_JSON_START=== and ===PLAN_JSON_END===. Do not write the article and do not fill the plan content.', 'smartypress-ai-engine' ),
			$language
		);

		$user_instruction = $this->get_processed_plan_instruction( $plan_prompt_template, $language );
		$user_prompt      = "USER_INSTRUCTION:\n" . $user_instruction;
		$user_prompt     .= "\n\nRUNTIME_DATA:\n" . wp_json_encode(
			array(
				'topic'     => $plan_context['topic'] ?? '',
				'context'   => $plan_context['context'] ?? '',
				'post_type' => $plan_context['post_type'] ?? 'post',
				'language'  => $language,
				'plan_expert_mode' => $expert_mode,
			),
			JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
		);
		$user_prompt     .= "\n\n" . __( 'Create only the editable schema for this specific plan. The base plan structure is fixed and must always include: article_title, meta_description, primary_keyword, secondary_keywords, and article_sections.', 'smartypress-ai-engine' );
		$user_prompt     .= "\n" . __( 'The article_sections field must be a repeater and must be locked so the whole section block cannot be removed. Base fields must be locked and required.', 'smartypress-ai-engine' );
		$user_prompt     .= "\n" . ( $expert_mode
			? __( 'Expert mode is enabled. Add these expert fields after the base fields: target_audience, search_intent, key_questions, content_angle, recommended_tone, structure_notes, practical_examples. Expert fields must not be locked so the editor can remove them manually.', 'smartypress-ai-engine' )
			: __( 'Expert mode is disabled. Do not add target audience, search intent, key questions, content angle, recommended tone, structure notes, practical examples, or other advanced editorial fields.', 'smartypress-ai-engine' )
		);
		$user_prompt     .= "\n" . __( 'Allowed field types: text, textarea, list, group, repeater. Each field must include key, label, and type. group and repeater fields must include fields. Use stable snake_case keys.', 'smartypress-ai-engine' );
		$user_prompt     .= "\n" . __( 'Return JSON with this shape: {"version":1,"fields":[...]}. No Markdown, no comments, no prose outside markers.', 'smartypress-ai-engine' );

		$params = array(
			'system_prompt'   => call_user_func( $cache_buster, $system_prompt ),
			'temperature'     => $options['temperature'] ?? 0.4,
			'max_tokens'      => min( max( absint( $options['max_tokens'] ?? 3000 ), 3000 ), 6000 ),
		) + $this->get_plan_request_defaults( 240 );
		$params = apply_filters( 'smartypress_ai_content_plan_schema_request_params', $params, $plan_context );

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

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

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

		$schema = $this->parser->parse_plan_json( $result['content'] ?? '', $plan_context );
		$schema = $this->normalizer->normalize_dynamic_plan_schema( $schema );
		$schema = $this->enforce_plan_schema_fields( $schema, $expert_mode );

		if ( empty( $schema['fields'] ) ) {
			return array(
				'success'    => false,
				'error'      => __( 'Failed to create the editable plan structure.', 'smartypress-ai-engine' ),
				'error_code' => 'plan_schema_failed',
			);
		}

		return array(
			'success' => true,
			'schema'  => $schema,
		);
	}

	/**
	 * Fill a dynamic plan schema with concrete values.
	 *
	 * @param object   $provider AI provider.
	 * @param array    $schema Normalized schema.
	 * @param string   $plan_prompt_template User-facing plan instruction.
	 * @param array    $plan_context Runtime context.
	 * @param array    $options Request options.
	 * @param string   $site_language Site language fallback.
	 * @param callable $cache_buster Cache buster callback.
	 * @return array
	 */
	public function generate_post_plan_from_schema( $provider, $schema, $plan_prompt_template, $plan_context, $options, $site_language, $cache_buster ) {
		$language = $options['language'] ?? $site_language;
		$expert_mode = ! empty( $options['plan_expert_mode'] ) || ! empty( $plan_context['plan_expert_mode'] );

		$system_prompt = sprintf(
			/* translators: %s: Language name. */
			__( 'You are an editorial planning assistant. Fill the provided editable plan schema in %s with concrete useful values. Return ONLY valid JSON wrapped between ===PLAN_JSON_START=== and ===PLAN_JSON_END===. Do not write the final article.', 'smartypress-ai-engine' ),
			$language
		);

		$user_instruction = $this->get_processed_plan_instruction( $plan_prompt_template, $language );
		$user_prompt      = "USER_INSTRUCTION:\n" . $user_instruction;
		$user_prompt     .= "\n\nPLAN_SCHEMA:\n" . wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
		$user_prompt     .= "\n\nRUNTIME_DATA:\n" . wp_json_encode(
			array(
				'topic'     => $plan_context['topic'] ?? '',
				'context'   => $plan_context['context'] ?? '',
				'post_type' => $plan_context['post_type'] ?? 'post',
				'language'  => $language,
				'plan_expert_mode' => $expert_mode,
			),
			JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
		);
		$user_prompt     .= "\n\n" . __( 'Return JSON with this shape: {"data":{...},"meta":{"draftInstruction":"short guidance for writing the final draft"}}. The data object must match PLAN_SCHEMA keys and nesting exactly.', 'smartypress-ai-engine' );
		$user_prompt     .= "\n" . __( 'Use real reader-facing titles, headings, questions, notes, keywords, and recommendations. Do not return instructions like "write ten titles"; return actual editable values.', 'smartypress-ai-engine' );
		$user_prompt     .= "\n" . __( 'No Markdown, no comments, no trailing commas, no prose outside the required markers.', 'smartypress-ai-engine' );

		$user_prompt = apply_filters( 'smartypress_ai_content_plan_prompt', $user_prompt, $plan_context );

		$params = array(
			'system_prompt'   => call_user_func( $cache_buster, $system_prompt ),
			'temperature'     => $options['temperature'] ?? 0.4,
			'max_tokens'      => ! empty( $options['max_tokens'] ) ? absint( $options['max_tokens'] ) : 8000,
		) + $this->get_plan_request_defaults( 300 );
		$params = apply_filters( 'smartypress_ai_content_plan_request_params', $params, $plan_context );

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

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

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

		$response = $this->parser->parse_plan_json( $result['content'] ?? '', $plan_context );

		if ( empty( $response ) ) {
			return array(
				'success'    => false,
				'error'      => __( 'Failed to parse the generated content plan.', 'smartypress-ai-engine' ),
				'error_code' => 'plan_parse_failed',
			);
		}

		return array(
			'success' => true,
			'plan'    => array(
				'schema' => $schema,
				'data'   => $response['data'] ?? $response,
				'meta'   => is_array( $response['meta'] ?? null ) ? $response['meta'] : array(),
			),
		);
	}

	/**
	 * Process user-facing plan instruction.
	 *
	 * @param string $plan_prompt_template Prompt template.
	 * @param string $language Language.
	 * @return string
	 */
	public function get_processed_plan_instruction( $plan_prompt_template, $language ) {
		if ( empty( $plan_prompt_template ) ) {
			return sprintf(
				/* translators: %s: language name. */
				__( 'Create an SEO-focused editorial plan in %s with the fields that are useful for this topic.', 'smartypress-ai-engine' ),
				$language
			);
		}

		return SmartyPress_AI_Prompt_Processor::process_prompt(
			$plan_prompt_template,
			array(
				'LANGUAGE' => $language,
			)
		);
	}

	/**
	 * Get conservative request defaults for dynamic plan generation.
	 *
	 * DeepSeek V4 Pro can spend a large part of the response budget on reasoning,
	 * so plan requests use JSON mode and explicitly disable thinking where the
	 * provider supports it.
	 *
	 * @param int $timeout Request timeout in seconds.
	 * @return array
	 */
	private function get_plan_request_defaults( $timeout ) {
		return array(
			'request_timeout' => absint( $timeout ),
			'response_format' => array(
				'type' => 'json_object',
			),
			'thinking'        => array(
				'type' => 'disabled',
			),
		);
	}

	/**
	 * Enforce the agreed base and expert schema fields.
	 *
	 * @param array $schema Normalized schema.
	 * @param bool  $expert_mode Whether expert mode is enabled.
	 * @return array
	 */
	private function enforce_plan_schema_fields( $schema, $expert_mode ) {
		$existing_fields = is_array( $schema['fields'] ?? null ) ? $schema['fields'] : array();
		$existing_by_key = array();

		foreach ( $existing_fields as $field ) {
			if ( is_array( $field ) && ! empty( $field['key'] ) ) {
				$existing_by_key[ $field['key'] ] = $field;
			}
		}

		$required_fields = $this->get_base_schema_fields();

		if ( $expert_mode ) {
			$required_fields = array_merge( $required_fields, $this->get_expert_schema_fields() );
		}

		$fields = array();

		foreach ( $required_fields as $required_field ) {
			$key      = $required_field['key'];
			$existing = $existing_by_key[ $key ] ?? array();

			// Required metadata wins so system fields cannot be removed accidentally.
			$fields[] = array_merge( $existing, $required_field );
		}

		return array(
			'version' => max( 1, absint( $schema['version'] ?? 1 ) ),
			'fields'  => $fields,
		);
	}

	/**
	 * Get required base schema fields.
	 *
	 * @return array
	 */
	private function get_base_schema_fields() {
		return array(
			array(
				'key'      => 'article_title',
				'label'    => __( 'Article Title', 'smartypress-ai-engine' ),
				'type'     => 'text',
				'locked'   => true,
				'required' => true,
			),
			array(
				'key'      => 'meta_description',
				'label'    => __( 'Meta Description (up to 160 characters)', 'smartypress-ai-engine' ),
				'type'     => 'textarea',
				'help'     => __( 'Short SEO page description up to 160 characters.', 'smartypress-ai-engine' ),
				'locked'   => true,
				'required' => true,
			),
			array(
				'key'      => 'primary_keyword',
				'label'    => __( 'Primary Keyword', 'smartypress-ai-engine' ),
				'type'     => 'text',
				'locked'   => true,
				'required' => true,
			),
			array(
				'key'      => 'secondary_keywords',
				'label'    => __( 'Secondary Keywords', 'smartypress-ai-engine' ),
				'type'     => 'list',
				'locked'   => true,
				'required' => true,
			),
			array(
				'key'      => 'article_sections',
				'label'    => __( 'Article Sections', 'smartypress-ai-engine' ),
				'type'     => 'repeater',
				'locked'   => true,
				'required' => true,
				'fields'   => array(
					array(
						'key'      => 'heading',
						'label'    => __( 'Section Heading', 'smartypress-ai-engine' ),
						'type'     => 'text',
						'locked'   => true,
						'required' => true,
					),
					array(
						'key'      => 'intent',
						'label'    => __( 'Section Goal', 'smartypress-ai-engine' ),
						'type'     => 'textarea',
						'locked'   => true,
						'required' => true,
					),
					array(
						'key'      => 'keywords',
						'label'    => __( 'Section Keywords', 'smartypress-ai-engine' ),
						'type'     => 'list',
						'locked'   => true,
						'required' => true,
					),
					array(
						'key'      => 'notes',
						'label'    => __( 'Section Notes', 'smartypress-ai-engine' ),
						'type'     => 'textarea',
						'locked'   => true,
						'required' => true,
					),
				),
			),
		);
	}

	/**
	 * Get optional expert schema fields.
	 *
	 * @return array
	 */
	private function get_expert_schema_fields() {
		return array(
			array(
				'key'    => 'target_audience',
				'label'  => __( 'Target Audience (description)', 'smartypress-ai-engine' ),
				'type'   => 'textarea',
				'expert' => true,
			),
			array(
				'key'    => 'search_intent',
				'label'  => __( 'Search Intent', 'smartypress-ai-engine' ),
				'type'   => 'text',
				'help'   => __( 'For example: informational, educational, commercial.', 'smartypress-ai-engine' ),
				'expert' => true,
			),
			array(
				'key'    => 'key_questions',
				'label'  => __( 'Key Questions the Article Answers', 'smartypress-ai-engine' ),
				'type'   => 'list',
				'expert' => true,
			),
			array(
				'key'    => 'content_angle',
				'label'  => __( 'Content Angle', 'smartypress-ai-engine' ),
				'type'   => 'textarea',
				'help'   => __( 'Unique perspective on the topic.', 'smartypress-ai-engine' ),
				'expert' => true,
			),
			array(
				'key'    => 'recommended_tone',
				'label'  => __( 'Recommended Tone', 'smartypress-ai-engine' ),
				'type'   => 'text',
				'help'   => __( 'For example: confident, accessible, inspiring.', 'smartypress-ai-engine' ),
				'expert' => true,
			),
			array(
				'key'    => 'structure_notes',
				'label'  => __( 'General Structure Notes', 'smartypress-ai-engine' ),
				'type'   => 'textarea',
				'help'   => __( 'What to include or exclude.', 'smartypress-ai-engine' ),
				'expert' => true,
			),
			array(
				'key'    => 'practical_examples',
				'label'  => __( 'Specific Practical Examples', 'smartypress-ai-engine' ),
				'type'   => 'list',
				'help'   => __( 'Examples to include in the text.', 'smartypress-ai-engine' ),
				'expert' => true,
			),
		);
	}
}
