<?php
/**
 * Costudio_Brand_Voice — Persistent brand voice management.
 *
 * Allows users to define a specific tone, persona, and rules per site.
 * This information is injected into the AI prompts to ensure stylistic consistency.
 *
 * @package Contentosapp_Studio
 * @since   3.2.0
 */

defined( 'ABSPATH' ) || exit;

class Costudio_Brand_Voice {

	const OPTION_KEY = 'costudio_brand_voices';

	public static function is_enabled(): bool {
		return true;
	}

	/**
	 * Retrieve the voice configuration for a specific site.
	 */
	public static function get( string $site_key ): ?array {
		$voices = (array) get_option( self::OPTION_KEY, [] );
		return $voices[ $site_key ] ?? null;
	}

	/**
	 * Persist a voice configuration.
	 */
	public static function save( string $site_key, array $voice ): bool {
		$voices = (array) get_option( self::OPTION_KEY, [] );
		$voices[ $site_key ] = self::sanitize( $voice );
		return update_option( self::OPTION_KEY, $voices, false );
	}

	/**
	 * Delete a voice configuration.
	 */
	public static function delete( string $site_key ): bool {
		$voices = (array) get_option( self::OPTION_KEY, [] );
		if ( isset( $voices[ $site_key ] ) ) {
			unset( $voices[ $site_key ] );
			return update_option( self::OPTION_KEY, $voices, false );
		}
		return false;
	}

	/**
	 * Clean and validate voice data before saving.
	 */
	public static function sanitize( array $raw ): array {
		$clean = [
			'name'           => sanitize_text_field( $raw['name'] ?? '' ),
			'schema_version' => 1,
			'updated_at'     => time(),
			'persona'        => [
				'author' => wp_kses_post( $raw['persona']['author'] ?? '' ),
				'reader' => wp_kses_post( $raw['persona']['reader'] ?? '' ),
			],
			'tone'           => [
				'formality'    => self::clamp( $raw['tone']['formality'] ?? 3, 1, 5 ),
				'technicality' => self::clamp( $raw['tone']['technicality'] ?? 3, 1, 5 ),
				'authority'    => self::clamp( $raw['tone']['authority'] ?? 3, 1, 5 ),
				'opinion'      => self::clamp( $raw['tone']['opinion'] ?? 3, 1, 5 ),
				'warmth'       => self::clamp( $raw['tone']['warmth'] ?? 3, 1, 5 ),
			],
			'vocabulary'     => [
				'preferred'    => self::sanitize_chips( $raw['vocabulary']['preferred'] ?? [] ),
				'avoid'        => self::sanitize_chips( $raw['vocabulary']['avoid'] ?? [] ),
				'signatures'   => self::sanitize_chips( $raw['vocabulary']['signatures'] ?? [] ),
				'replacements' => self::sanitize_replacements( $raw['vocabulary']['replacements'] ?? [] ),
			],
			'rules'          => [
				'pov'            => sanitize_key( $raw['rules']['pov'] ?? 'third' ),
				'paragraph_len'  => sanitize_key( $raw['rules']['paragraph_len'] ?? 'medium' ),
				'use_emojis'     => (bool) ( $raw['rules']['use_emojis'] ?? false ),
				'h2_as_question' => (bool) ( $raw['rules']['h2_as_question'] ?? false ),
				'dos'            => self::sanitize_chips( $raw['rules']['dos'] ?? [] ),
				'donts'          => self::sanitize_chips( $raw['rules']['donts'] ?? [] ),
			],
			'samples'        => self::sanitize_samples( $raw['samples'] ?? [] ),
		];

		return $clean;
	}

	/**
	 * Helper to generate an immutable snapshot for a production run.
	 */
	public static function snapshot_for_production( string $site_key ): ?array {
		$voice = self::get( $site_key );
		if ( ! $voice || ! self::is_enabled() ) {
			return null;
		}
		// We return the full array to be stored in production inputs.
		return $voice;
	}

	/**
	 * Core Engine: Translates settings into a technical AI block.
	 */
	public static function render_prompt_block( array $voice, int $agent, string $lang = 'pt_BR', string $mode = 'full' ): string {
		$t = [
			'pt_BR' => [
				'header'      => 'DIRETRIZES DE VOZ DA MARCA (Obrigatório)',
				'persona'     => 'Persona do Autor: %s',
				'reader'      => 'Público-alvo: %s',
				'tone'        => 'Tom de Voz (Escala 1-5):',
				'formality'   => '- Formalidade: %d/5',
				'tech'        => '- Tecnicidade: %d/5',
				'auth'        => '- Autoridade: %d/5',
				'opinion'     => '- Opinião: %d/5',
				'warmth'      => '- Calor Humano: %d/5',
				'vocab'       => 'Vocabulário e Estilo:',
				'pref'        => '- Termos preferidos: %s',
				'avoid'       => '- Termos a evitar: %s',
				'signatures'  => '- Frases de assinatura: %s',
				'replace'     => '- Substituições obrigatórias: %s',
				'samples'     => 'Exemplos de voz:',
				'sample_item' => '- Exemplo %d: %s',
				'rules'       => 'Regras de Escrita:',
				'pov'         => '- Ponto de vista: %s',
				'para'        => '- Tamanho de parágrafo: %s',
				'emojis'      => '- Uso de emojis: %s',
				'h2_q'        => '- Headings H2 como perguntas: %s',
				'dos'         => '- O QUE FAZER: %s',
				'donts'       => '- O QUE NÃO FAZER: %s',
				'yes'         => 'Sim',
				'no'          => 'Não',
				'pov_map'     => [
					'first_plural' => '1ª pessoa do plural (Nós)',
					'second'       => '2ª pessoa (Você)',
					'third'        => '3ª pessoa (Impessoal)',
				],
				'len_map'     => [
					'short'  => 'Curtos e diretos',
					'medium' => 'Equilibrados',
					'long'   => 'Longos e explicativos',
				],
			],
			'en_US' => [
				'header'      => 'BRAND VOICE GUIDELINES (Mandatory)',
				'persona'     => 'Author Persona: %s',
				'reader'      => 'Target Audience: %s',
				'tone'        => 'Tone of Voice (Scale 1-5):',
				'formality'   => '- Formality: %d/5',
				'tech'        => '- Technicality: %d/5',
				'auth'        => '- Authority: %d/5',
				'opinion'     => '- Opinion: %d/5',
				'warmth'      => '- Warmth: %d/5',
				'vocab'       => 'Vocabulary and Style:',
				'pref'        => '- Preferred terms: %s',
				'avoid'       => '- Terms to avoid: %s',
				'signatures'  => '- Signature phrases: %s',
				'replace'     => '- Required substitutions: %s',
				'samples'     => 'Voice examples:',
				'sample_item' => '- Example %d: %s',
				'rules'       => 'Writing Rules:',
				'pov'         => '- Point of View: %s',
				'para'        => '- Paragraph Length: %s',
				'emojis'      => '- Use emojis: %s',
				'h2_q'        => '- H2 headings as questions: %s',
				'dos'         => '- DO: %s',
				'donts'       => '- DO NOT: %s',
				'yes'         => 'Yes',
				'no'          => 'No',
				'pov_map'     => [
					'first_plural' => '1st person plural (We)',
					'second'       => '2nd person (You)',
					'third'        => '3rd person (Impersonal)',
				],
				'len_map'     => [
					'short'  => 'Short and direct',
					'medium' => 'Balanced',
					'long'   => 'Long and explanatory',
				],
			],
			'es_ES' => [
				'header'      => 'DIRECTRICES DE VOZ DE MARCA (Obligatorio)',
				'persona'     => 'Persona del Autor: %s',
				'reader'      => 'Público objetivo: %s',
				'tone'        => 'Tono de voz (Escala 1-5):',
				'formality'   => '- Formalidad: %d/5',
				'tech'        => '- Tecnicidad: %d/5',
				'auth'        => '- Autoridad: %d/5',
				'opinion'     => '- Opinión: %d/5',
				'warmth'      => '- Calidez: %d/5',
				'vocab'       => 'Vocabulario y Estilo:',
				'pref'        => '- Términos preferidos: %s',
				'avoid'       => '- Términos a evitar: %s',
				'signatures'  => '- Frases de firma: %s',
				'replace'     => '- Sustituciones obligatorias: %s',
				'samples'     => 'Ejemplos de voz:',
				'sample_item' => '- Ejemplo %d: %s',
				'rules'       => 'Reglas de Escritura:',
				'pov'         => '- Punto de vista: %s',
				'para'        => '- Longitud de párrafo: %s',
				'emojis'      => '- Uso de emojis: %s',
				'h2_q'        => '- Encabezados H2 como preguntas: %s',
				'dos'         => '- QUÉ HACER: %s',
				'donts'       => '- QUÉ NO HACER: %s',
				'yes'         => 'Sí',
				'no'          => 'No',
				'pov_map'     => [
					'first_plural' => '1ª persona del plural (Nosotros)',
					'second'       => '2ª persona (Usted/Tú)',
					'third'        => '3ª persona (Impersonal)',
				],
				'len_map'     => [
					'short'  => 'Cortos y directos',
					'medium' => 'Equilibrados',
					'long'   => 'Largos y explicativos',
				],
			],
		];

		$i18n = $t[ $lang ] ?? $t['en_US'];
		$out  = [ '## ' . $i18n['header'] ];

		if ( ! empty( $voice['persona']['author'] ) ) {
			$out[] = sprintf( $i18n['persona'], $voice['persona']['author'] );
		}
		if ( ! empty( $voice['persona']['reader'] ) ) {
			$out[] = sprintf( $i18n['reader'], $voice['persona']['reader'] );
		}

		// Tone dimensions are skipped in vocabulary_only mode (used for News articles
		// where tone should emerge from sources, not be imposed by brand directives).
		if ( 'vocabulary_only' !== $mode ) {
			$out[] = '';
			$out[] = $i18n['tone'];
			$out[] = sprintf( $i18n['formality'], $voice['tone']['formality'] );
			$out[] = sprintf( $i18n['tech'], $voice['tone']['technicality'] );
			$out[] = sprintf( $i18n['auth'], $voice['tone']['authority'] );
			$out[] = sprintf( $i18n['opinion'], $voice['tone']['opinion'] );
			$out[] = sprintf( $i18n['warmth'], $voice['tone']['warmth'] );
		}

		$out[] = '';
		$out[] = $i18n['vocab'];
		if ( ! empty( $voice['vocabulary']['preferred'] ) ) {
			$out[] = sprintf( $i18n['pref'], implode( ', ', $voice['vocabulary']['preferred'] ) );
		}
		if ( ! empty( $voice['vocabulary']['avoid'] ) ) {
			$out[] = sprintf( $i18n['avoid'], implode( ', ', $voice['vocabulary']['avoid'] ) );
		}
		if ( ! empty( $voice['vocabulary']['signatures'] ) ) {
			$out[] = sprintf( $i18n['signatures'], implode( '; ', $voice['vocabulary']['signatures'] ) );
		}
		if ( ! empty( $voice['vocabulary']['replacements'] ) && is_array( $voice['vocabulary']['replacements'] ) ) {
			$replacements = [];
			foreach ( $voice['vocabulary']['replacements'] as $replacement ) {
				if ( empty( $replacement['from'] ) || empty( $replacement['to'] ) ) {
					continue;
				}
				$replacements[] = sprintf( '"%s" → "%s"', $replacement['from'], $replacement['to'] );
			}
			if ( ! empty( $replacements ) ) {
				$out[] = sprintf( $i18n['replace'], implode( '; ', $replacements ) );
			}
		}

		$out[] = '';
		$out[] = $i18n['rules'];
		$out[] = sprintf( $i18n['pov'], $i18n['pov_map'][ $voice['rules']['pov'] ] ?? $voice['rules']['pov'] );
		$out[] = sprintf( $i18n['para'], $i18n['len_map'][ $voice['rules']['paragraph_len'] ] ?? $voice['rules']['paragraph_len'] );
		$out[] = sprintf( $i18n['emojis'], $voice['rules']['use_emojis'] ? $i18n['yes'] : $i18n['no'] );
		$out[] = sprintf( $i18n['h2_q'], $voice['rules']['h2_as_question'] ? $i18n['yes'] : $i18n['no'] );

		if ( ! empty( $voice['rules']['dos'] ) ) {
			$out[] = sprintf( $i18n['dos'], implode( '; ', $voice['rules']['dos'] ) );
		}
		if ( ! empty( $voice['rules']['donts'] ) ) {
			$out[] = sprintf( $i18n['donts'], implode( '; ', $voice['rules']['donts'] ) );
		}

		if ( ! empty( $voice['samples'] ) && is_array( $voice['samples'] ) ) {
			$out[] = '';
			$out[] = $i18n['samples'];
			foreach ( array_values( $voice['samples'] ) as $index => $sample ) {
				if ( '' === trim( (string) $sample ) ) {
					continue;
				}
				$out[] = sprintf( $i18n['sample_item'], $index + 1, $sample );
			}
		}

		// Agent 5 (Editorial Reviewer): append an explicit voice-alignment evaluation directive.
		// The A05 prompt receives this block and should use it as a compliance checklist.
		if ( 5 === $agent ) {
			$out[] = '';
			$out[] = '### VOICE ALIGNMENT CHECK (A05 — Mandatory)';
			$out[] = 'Evaluate whether the article strictly follows the Brand Voice directives above. In your feedback field, include a voice compliance note covering: (1) any forbidden terms found in the text, (2) whether the formality and authority levels match the brand scale, (3) whether the author persona is perceptible in the writing style. If violations are found, set voice_misaligned=true in your response JSON.';
		}

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

	// ── Private Helpers ──────────────────────────────────────────────────────

	private static function clamp( $val, int $min, int $max ): int {
		return max( $min, min( $max, (int) $val ) );
	}

	private static function sanitize_chips( $chips ): array {
		$arr = is_array( $chips ) ? $chips : explode( ',', (string) $chips );
		return array_filter( array_map( 'sanitize_text_field', array_map( 'trim', $arr ) ) );
	}

	private static function sanitize_replacements( $items ): array {
		$items = is_array( $items ) ? $items : [];
		$clean = [];
		$seen  = [];

		foreach ( $items as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}

			$from = sanitize_text_field( trim( (string) ( $item['from'] ?? '' ) ) );
			$to   = sanitize_text_field( trim( (string) ( $item['to'] ?? '' ) ) );

			if ( '' === $from || '' === $to ) {
				continue;
			}

			$key = strtolower( $from );
			if ( isset( $seen[ $key ] ) ) {
				continue;
			}

			$seen[ $key ] = true;
			$clean[]      = [
				'from' => $from,
				'to'   => $to,
			];

			if ( count( $clean ) >= 10 ) {
				break;
			}
		}

		return $clean;
	}

	private static function sanitize_samples( $samples ): array {
		$samples = array_slice( (array) $samples, 0, 3 );
		$clean   = [];

		foreach ( $samples as $sample ) {
			$text = sanitize_textarea_field( (string) $sample );
			if ( '' !== $text ) {
				$clean[] = $text;
			}
		}

		return $clean;
	}
}
