<?php
/**
 * Template Parser for BoostBot.
 *
 * Parses AI responses and replaces template tags with rendered HTML.
 *
 * @package BoostBot
 * @since 1.1.0
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Class BoostBot_Template_Parser
 *
 * Parses AI response content for template tags and replaces them with
 * rendered template HTML.
 *
 * @since 1.1.0
 */
class BoostBot_Template_Parser {

    /**
     * Template manager instance.
     *
     * @var BoostBot_Template_Manager
     */
    private $template_manager;

    /**
     * Constructor.
     */
    public function __construct() {
        $this->template_manager = BoostBot_Template_Manager::get_instance();
    }

    /**
     * Parse AI response content for template tags.
     *
     * Template tags have the format: {template:TEMPLATE_ID|JSON_DATA}
     *
     * @param string $content The AI response content.
     * @return string Content with template tags replaced by rendered HTML.
     */
    public function parse( string $content ): string {
        // Pattern matches: {template:template-id|{...json...}}
        // The regex captures:
        // 1. Template ID (alphanumeric with hyphens)
        // 2. JSON data (everything between the | and the closing })
        $pattern = '/\{template:([a-z0-9-]+)\|(\{.+?\})\}/s';

        return preg_replace_callback( $pattern, [ $this, 'replace_template_tag' ], $content );
    }

    /**
     * Callback for preg_replace_callback to replace a single template tag.
     *
     * @param array $matches Regex matches.
     * @return string Rendered template HTML or error comment.
     */
    private function replace_template_tag( array $matches ): string {
        $template_id = $matches[1];
        $json_string = $matches[2];

        // Try to decode the JSON
        $data = json_decode( $json_string, true );

        if ( json_last_error() !== JSON_ERROR_NONE ) {
            BoostBot_Logger::warning( 'Template JSON parse error', [
                'template'    => $template_id,
                'json'        => substr( $json_string, 0, 200 ),
                'error'       => json_last_error_msg(),
            ]);
            return $this->render_error( $template_id, 'Invalid JSON: ' . json_last_error_msg() );
        }

        // Check if template exists
        if ( ! $this->template_manager->template_exists( $template_id ) ) {
            BoostBot_Logger::warning( 'Unknown template requested', [
                'template' => $template_id,
            ]);
            return $this->render_error( $template_id, 'Unknown template' );
        }

        // Render the template (access check is done in template_manager->render)
        try {
            return $this->template_manager->render( $template_id, $data );
        } catch ( \Exception $e ) {
            BoostBot_Logger::error( 'Template render error', [
                'template' => $template_id,
                'error'    => $e->getMessage(),
            ]);
            return $this->render_error( $template_id, 'Render error' );
        }
    }

    /**
     * Check if content contains any template tags.
     *
     * @param string $content The content to check.
     * @return bool True if content has template tags.
     */
    public function has_templates( string $content ): bool {
        return preg_match( '/\{template:[a-z0-9-]+\|/', $content ) === 1;
    }

    /**
     * Extract all template tags from content (without rendering).
     *
     * Useful for analytics or debugging.
     *
     * @param string $content The content to parse.
     * @return array Array of [ 'template_id' => string, 'data' => array ].
     */
    public function extract_templates( string $content ): array {
        $pattern = '/\{template:([a-z0-9-]+)\|(\{.+?\})\}/s';
        $templates = [];

        if ( preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER ) ) {
            foreach ( $matches as $match ) {
                $data = json_decode( $match[2], true );
                $templates[] = [
                    'template_id' => $match[1],
                    'data'        => $data ?? [],
                    'valid_json'  => $data !== null,
                ];
            }
        }

        return $templates;
    }

    /**
     * Count templates in content.
     *
     * @param string $content The content to check.
     * @return int Number of template tags found.
     */
    public function count_templates( string $content ): int {
        $pattern = '/\{template:[a-z0-9-]+\|/';
        return preg_match_all( $pattern, $content );
    }

    /**
     * Render an error message for failed template parsing.
     *
     * @param string $template_id The template that failed.
     * @param string $error       Error message.
     * @return string HTML error comment (invisible in rendered output).
     */
    private function render_error( string $template_id, string $error ): string {
        // Return an HTML comment for debugging (invisible to users)
        return '<!-- Template error [' . esc_html( $template_id ) . ']: ' . esc_html( $error ) . ' -->';
    }

    /**
     * Validate template JSON structure before rendering.
     *
     * @param string $template_id The template identifier.
     * @param array  $data        The parsed JSON data.
     * @return bool|string True if valid, error message if not.
     */
    public function validate_template_data( string $template_id, array $data ) {
        $template = $this->template_manager->get_template( $template_id );

        if ( ! $template ) {
            return 'Unknown template';
        }

        $schema = $template['schema'] ?? [];
        $missing_required = [];

        foreach ( $schema as $field => $rules ) {
            if ( ! empty( $rules['required'] ) && ! isset( $data[ $field ] ) ) {
                $missing_required[] = $field;
            }
        }

        if ( ! empty( $missing_required ) ) {
            return 'Missing required fields: ' . implode( ', ', $missing_required );
        }

        return true;
    }
}
