<?php
defined( 'ABSPATH' ) || exit;

/**
 * Builds the AI prompt and parses the structured JSON response.
 * Supports content depth (express/standard/rich), tone/style selection,
 * Guide Mode (How To only), How To formatting templates, and output language.
 */
class PIPEBU_Generator {

    private static array $language_names = [
        'en' => 'English',
        'es' => 'Spanish',
        'pt' => 'Portuguese',
        'fr' => 'French',
        'de' => 'German',
        'it' => 'Italian',
        'ja' => 'Japanese',
        'zh' => 'Chinese (Simplified)',
        'ko' => 'Korean',
        'ar' => 'Arabic',
    ];

    /**
     * Run full analysis on a set of images.
     *
     * @param int[]  $attachment_ids
     * @param string $depth           express|standard|rich
     * @param string $tone            professional|friendly|technical|playful|philosophical|custom
     * @param string $custom_style    User's custom style brief (only when tone=custom)
     * @param bool   $guide_mode      True = How To only, skip product content
     * @param string $template        none|step_by_step|narrative|quick_start
     * @param string $output_language ISO code: en|es|pt|fr|de|it|ja|zh|ko|ar — empty = auto
     * @return array|WP_Error
     */
    public static function run(
        array $attachment_ids,
        string $depth = 'standard',
        string $tone = 'professional',
        string $custom_style = '',
        bool $guide_mode = false,
        string $template = 'none',
        string $tone_reference = '',
        bool $has_colors = false,
        string $output_language = ''
    ): array|WP_Error {
        $paths = self::ids_to_paths( $attachment_ids );
        if ( is_wp_error( $paths ) ) {
            return $paths;
        }

        $prompt = $guide_mode
            ? self::build_guide_prompt( $depth, $tone, $custom_style, $template, $tone_reference, $output_language )
            : self::build_prompt( $depth, $tone, $custom_style, $template, $tone_reference, $has_colors, $output_language );

        $raw = PIPEBU_AI::analyze( $paths, $prompt );

        if ( is_wp_error( $raw ) ) {
            return $raw;
        }

        return $guide_mode ? self::parse_guide( $raw ) : self::parse( $raw );
    }

    // -------------------------------------------------------------------------
    // Color detection — second AI call, independent of main description.
    // Kept for potential future use. Not called in the main pipeline.
    // Returns [ ['name' => string, 'image_index' => int], … ] or WP_Error.
    // -------------------------------------------------------------------------

    public static function detect_colors( array $image_paths ): array|WP_Error {
        $prompt = 'Look at these product images. Each image shows the product in a different color variant. '
            . 'Return a JSON array — one object per distinct color you can identify: '
            . '[ {"name": "Color Name", "image_index": 0}, ... ] '
            . 'Rules: use clean, marketable color names (e.g. "Midnight Black", "Pearl White", "Gunmetal Gray"). '
            . 'image_index is zero-based and matches the order of images provided. '
            . 'If multiple images show the same color, use the first occurrence. '
            . 'If only one color is visible across all images, return a single-item array. '
            . 'Return ONLY the JSON array. No markdown, no other text.';

        $raw = PIPEBU_AI::analyze( $image_paths, $prompt );
        if ( is_wp_error( $raw ) ) {
            return $raw;
        }

        $clean = preg_replace( '/^```(?:json)?\s*/i', '', trim( $raw ) );
        $clean = preg_replace( '/\s*```$/i', '', $clean );
        $data  = json_decode( $clean, true );

        if ( ! is_array( $data ) ) {
            $start = strpos( $clean, '[' );
            $end   = strrpos( $clean, ']' );
            if ( $start !== false && $end !== false && $end > $start ) {
                $data = json_decode( substr( $clean, $start, $end - $start + 1 ), true );
            }
        }

        if ( ! is_array( $data ) ) {
            return new WP_Error( 'pipebu_color_parse', 'Could not parse color detection response.' );
        }

        $colors = [];
        foreach ( $data as $item ) {
            $name = sanitize_text_field( $item['name'] ?? '' );
            if ( $name ) {
                $colors[] = [
                    'name'        => $name,
                    'image_index' => max( 0, (int) ( $item['image_index'] ?? 0 ) ),
                ];
            }
        }

        return $colors;
    }

    // -------------------------------------------------------------------------
    // Prompt builder — standard mode (product + How To)
    // -------------------------------------------------------------------------

    private static function build_prompt(
        string $depth,
        string $tone,
        string $custom_style,
        string $template = 'none',
        string $tone_reference = '',
        bool $has_colors = false,
        string $output_language = ''
    ): string {

        $depth_instructions = [
            'express'  => "Write CONCISE content. Short descriptions (1 sentence). Features as tight bullet points (5 max). Full description under 200 words. How To guide under 400 words. Scannable, conversion-focused. No filler.",
            'standard' => "Write BALANCED content. Short description 1-2 sentences. Features 6-8 bullet points. Full description 300-500 words. How To guide 600-900 words.",
            'rich'     => "Write RICH, content-dense copy. Short description 2-3 sentences. Features 8-12 detailed bullet points. Full description 600-900 words with thorough specs and context. How To guide 1000-1400 words, comprehensive. Maximize indexable content for SEO.",
        ];

        $lang_name_early = self::$language_names[ $output_language ] ?? '';
        $custom_lang_note = $lang_name_early
            ? " The brief below may be written in a different language — ignore that language and write all output in {$lang_name_early}. The brief describes STYLE ONLY."
            : '';

        $tone_instructions = [
            'professional'  => "Tone: Professional and authoritative. Clean sentences. Confident statements. No slang.",
            'friendly'      => "Tone: Warm and approachable. Write like a helpful friend explaining to a beginner. Use 'you' naturally. Encouraging.",
            'technical'     => "Tone: Technical and precise. Lead with specs. Use correct terminology. Enthusiast audience who wants the details.",
            'playful'       => "Tone: Playful and fun. Personality-driven. Light humor where appropriate. Lifestyle brand feel. Energetic.",
            'philosophical' => "Tone: Thoughtful and reflective. Connect the product to a larger idea or experience. Elevate the mundane. Make the reader feel something.",
            'custom'        => "Tone: Apply this voice and style to the written content fields only — the response format must still be a raw JSON object with no other text.{$custom_lang_note} Brief: " . sanitize_text_field( $custom_style ),
        ];

        $template_instructions = self::howto_template_instruction( $template );

        $depth_inst    = $depth_instructions[ $depth ] ?? $depth_instructions['standard'];
        $tone_inst     = $tone_instructions[ $tone ]   ?? $tone_instructions['professional'];
        $template_inst = $template_instructions ? "\n\nHow To structure: {$template_instructions}" : '';
        $ref_inst      = $tone_reference
            ? "\n\nStyle Reference — study this example and match its voice, rhythm, and format closely:\n---\n{$tone_reference}\n---\nWrite in the same style as the above example."
            : '';

        $colors_schema = $has_colors
            ? '  "colors": [{"name": "Clean marketable color name e.g. Midnight Black", "image_index": 0}],'
            : '  "colors": [],';

        $json_schema = implode( "\n", [
            '{',
            '  "product_name": "Full product name as shown on packaging",',
            '  "short_description": "HTML. Benefit-focused. Length per depth setting.",',
            '  "description": "Full HTML WooCommerce product description. Structure: intro paragraph → <h3>Key Features</h3><ul> → <h3>What\'s In The Box</h3><ul> → <h3>Specifications</h3><ul> → closing <p><em>italic compatibility note</em></p>. Length per depth setting. Clean HTML only.",',
            '  "features": ["Feature 1", "Feature 2"],',
            '  "specs": {"Spec Name": "Value"},',
            '  "box_contents": ["Item 1", "Item 2"],',
            '  "yoast_focus_keyword": "product name + primary use, lowercase, max 5 words",',
            '  "yoast_meta_description": "Max 155 characters. Natural language. Benefit-focused. No keyword stuffing.",',
            '  "howto_title": "How to Use the [Product Name] — Complete Guide",',
            '  "howto_content": "Full HTML for a WordPress How To post. Length per depth setting. Clean HTML only.",',
            '  "howto_meta_description": "Max 155 characters. Describes the How To post specifically.",',
            '  "suggested_categories": ["Category name 1", "Category name 2"],',
            '  "is_variable": false,',
            '  "variations_hint": "If is_variable true: describe the variations found in images",',
            $colors_schema,
            '  "confidence_notes": "Note any fields where info was unclear or missing. Empty string if all clear."',
            '}',
        ] );

        $colors_inst = $has_colors
            ? "\n\nColor variants: each uploaded image shows the product in a different color. "
              . "Populate the 'colors' array with one entry per image — use a clean, marketable color name "
              . "(e.g. 'Midnight Black', 'Pearl White', 'Gunmetal Gray') and the zero-based index of the image showing that color. "
              . "Set is_variable to true."
            : '';

        $lang_name = self::$language_names[ $output_language ] ?? '';
        $lang_inst = $lang_name
            ? "\n\nOutput language: {$lang_name}. Write ALL text content fields in {$lang_name} — short_description, description, features, box_contents, howto_title, howto_content, meta descriptions, yoast fields, confidence_notes, everything. No English unless the product name itself is a proper noun brand name."
            : '';

        return 'You are an expert WooCommerce product content writer.' . "\n\n"
            . $depth_inst . "\n\n"
            . $tone_inst . $ref_inst . $template_inst . "\n\n"
            . "Analyze all provided images carefully. Images may include:\n"
            . "- Product exterior photos (multiple angles)\n"
            . "- Packaging (box front, back, sides \xe2\x80\x94 often contain specs and box contents)\n"
            . "- Manual or instruction pages (step-by-step usage, settings, safety notes)\n"
            . "- Specification sheets\n\n"
            . "Extract ALL available information from every image and return a single valid JSON object using this exact structure. Do NOT include markdown, code fences, or any text outside the JSON object. CRITICAL: within any JSON string value, never use unescaped double-quote characters — use single quotes instead if quoting within content.\n\n"
            . $json_schema . $colors_inst . $lang_inst . "\n\n"
            . "If a field cannot be determined from the images, use null for that field (except arrays/objects \xe2\x80\x94 use [] or {} for those).\n"
            . 'Return ONLY the JSON object. No other text.';
    }

    // -------------------------------------------------------------------------
    // Prompt builder — Guide Mode (How To only)
    // -------------------------------------------------------------------------

    private static function build_guide_prompt(
        string $depth,
        string $tone,
        string $custom_style,
        string $template = 'none',
        string $tone_reference = '',
        string $output_language = ''
    ): string {

        $depth_instructions = [
            'express'  => "Write a CONCISE How To guide. Under 400 words. Numbered steps only. Scannable.",
            'standard' => "Write a BALANCED How To guide. 600-900 words. Clear sections.",
            'rich'     => "Write a COMPREHENSIVE How To guide. 1000-1400 words. Full sections, thorough explanations. Maximize indexable content.",
        ];

        $lang_name_early = self::$language_names[ $output_language ] ?? '';
        $custom_lang_note = $lang_name_early
            ? " The brief below may be written in a different language — ignore that language and write all output in {$lang_name_early}. The brief describes STYLE ONLY."
            : '';

        $tone_instructions = [
            'professional'  => "Tone: Professional and authoritative. Clean sentences. Confident statements.",
            'friendly'      => "Tone: Warm and approachable. Write like a helpful friend. Use 'you' naturally.",
            'technical'     => "Tone: Technical and precise. Lead with specs. Correct terminology throughout.",
            'playful'       => "Tone: Playful and fun. Personality-driven. Light humor where appropriate.",
            'philosophical' => "Tone: Thoughtful and reflective. Connect usage to a larger experience.",
            'custom'        => "Tone: Apply this voice and style to the written content fields only — the response format must still be a raw JSON object with no other text.{$custom_lang_note} Brief: " . sanitize_text_field( $custom_style ),
        ];

        $template_instructions = self::howto_template_instruction( $template );

        $depth_inst    = $depth_instructions[ $depth ] ?? $depth_instructions['standard'];
        $tone_inst     = $tone_instructions[ $tone ]   ?? $tone_instructions['professional'];
        $template_inst = $template_instructions ? "\n\nStructure: {$template_instructions}" : "\n\nStructure: intro paragraph → <h2>Before You Start</h2> → <h2>Step-by-Step Guide</h2><ol> → <h2>Tips & Settings</h2> → <h2>Cleaning & Maintenance</h2> → closing paragraph with placeholder links [PRODUCT_LINK] and [PARTS_LINK].";
        $ref_inst      = $tone_reference
            ? "\n\nStyle Reference — study this example and match its voice, rhythm, and format closely:\n---\n{$tone_reference}\n---\nWrite in the same style as the above example."
            : '';

        $json_schema = implode( "\n", [
            '{',
            '  "howto_title": "How to Use the [Product Name] — Complete Guide",',
            '  "howto_content": "Full HTML for a WordPress How To post. Use the specified structure. Include placeholder links [PRODUCT_LINK] and [PARTS_LINK] in the closing paragraph. Clean HTML only.",',
            '  "howto_meta_description": "Max 155 characters. Describes the How To post. Natural language.",',
            '  "yoast_focus_keyword": "product name + how to use, lowercase, max 5 words",',
            '  "confidence_notes": "Note any fields where info was unclear or missing. Empty string if all clear."',
            '}',
        ] );

        $lang_name = self::$language_names[ $output_language ] ?? '';
        $lang_inst = $lang_name
            ? "\n\nOutput language: {$lang_name}. Write ALL text content fields in {$lang_name} — howto_title, howto_content, meta descriptions, yoast fields, confidence_notes, everything. No English unless the product name itself is a proper noun brand name."
            : '';

        return 'You are an expert product content writer specializing in How To guides.' . "\n\n"
            . $depth_inst . "\n\n"
            . $tone_inst . $ref_inst . $template_inst . "\n\n"
            . "Analyze all provided images carefully. Images may include manual pages, instruction sheets, product photos, packaging, or specification sheets. Extract ALL available information.\n\n"
            . "Return a single valid JSON object using this exact structure. Do NOT include markdown, code fences, or any text outside the JSON object. CRITICAL: within any JSON string value, never use unescaped double-quote characters — use single quotes instead if quoting within content.\n\n"
            . $json_schema . $lang_inst . "\n\n"
            . 'Return ONLY the JSON object. No other text.';
    }

    // -------------------------------------------------------------------------
    // How To template structure instructions (shared by both prompts)
    // -------------------------------------------------------------------------

    private static function howto_template_instruction( string $template ): string {
        $templates = [
            'step_by_step' => "intro paragraph → <h2>Specifications</h2> with a <table> of key specs → <h2>Step-by-Step Guide</h2> with a numbered <ol> for each step → <h2>Tips & Settings</h2> with a <ul> → <h2>Cleaning & Maintenance</h2> with a <ul> → closing <p> with [PRODUCT_LINK] and [PARTS_LINK].",
            'narrative'    => "warm intro paragraph connecting to the user experience → narrative walkthrough sections in prose (no numbered lists) with specs woven naturally into the copy → tips and best practices in conversational tone → closing <p> with [PRODUCT_LINK] and [PARTS_LINK].",
            'quick_start'  => "one-sentence intro → <h2>Key Specs</h2> <ul> (5 items max) → <h2>Quick Start</h2> <ol> (5 steps max, each under 20 words) → <h2>Pro Tips</h2> <ul> (3 bullets max) → closing one-liner with [PRODUCT_LINK].",
            'none'         => '',
        ];
        return $templates[ $template ] ?? '';
    }

    // -------------------------------------------------------------------------
    // Response parsing — standard mode
    // -------------------------------------------------------------------------

    private static function parse( string $raw ): array|WP_Error {
        $data = self::decode( $raw );
        if ( is_wp_error( $data ) ) return $data;

        return [
            'product_name'           => sanitize_text_field( $data['product_name'] ?? '' ),
            'short_description'      => wp_kses_post( $data['short_description'] ?? '' ),
            'description'            => wp_kses_post( $data['description'] ?? '' ),
            'features'               => array_map( 'sanitize_text_field', (array) ( $data['features'] ?? [] ) ),
            'specs'                  => array_map( 'sanitize_text_field', (array) ( $data['specs'] ?? [] ) ),
            'box_contents'           => array_map( 'sanitize_text_field', (array) ( $data['box_contents'] ?? [] ) ),
            'yoast_focus_keyword'    => sanitize_text_field( $data['yoast_focus_keyword'] ?? '' ),
            'yoast_meta_description' => sanitize_text_field( $data['yoast_meta_description'] ?? '' ),
            'howto_title'            => sanitize_text_field( $data['howto_title'] ?? '' ),
            'howto_content'          => wp_kses_post( $data['howto_content'] ?? '' ),
            'howto_meta_description' => sanitize_text_field( $data['howto_meta_description'] ?? '' ),
            'suggested_categories'   => array_map( 'sanitize_text_field', (array) ( $data['suggested_categories'] ?? [] ) ),
            'is_variable'            => (bool) ( $data['is_variable'] ?? false ),
            'variations_hint'        => sanitize_text_field( $data['variations_hint'] ?? '' ),
            'colors'                 => self::parse_colors( $data['colors'] ?? [] ),
            'confidence_notes'       => sanitize_text_field( $data['confidence_notes'] ?? '' ),
        ];
    }

    // -------------------------------------------------------------------------
    // Response parsing — Guide Mode (How To only fields)
    // -------------------------------------------------------------------------

    private static function parse_guide( string $raw ): array|WP_Error {
        $data = self::decode( $raw );
        if ( is_wp_error( $data ) ) return $data;

        return [
            // How To fields — populated
            'howto_title'            => sanitize_text_field( $data['howto_title'] ?? '' ),
            'howto_content'          => wp_kses_post( $data['howto_content'] ?? '' ),
            'howto_meta_description' => sanitize_text_field( $data['howto_meta_description'] ?? '' ),
            'yoast_focus_keyword'    => sanitize_text_field( $data['yoast_focus_keyword'] ?? '' ),
            'confidence_notes'       => sanitize_text_field( $data['confidence_notes'] ?? '' ),
            // Product fields — empty (Guide Mode skips product creation)
            'product_name'           => '',
            'short_description'      => '',
            'description'            => '',
            'features'               => [],
            'specs'                  => [],
            'box_contents'           => [],
            'yoast_meta_description' => '',
            'suggested_categories'   => [],
            'is_variable'            => false,
            'variations_hint'        => '',
        ];
    }

    // -------------------------------------------------------------------------
    // Parse colors array from AI response field
    // -------------------------------------------------------------------------

    private static function parse_colors( mixed $raw ): array {
        if ( ! is_array( $raw ) ) {
            return [];
        }
        $colors = [];
        foreach ( $raw as $item ) {
            $name = sanitize_text_field( $item['name'] ?? '' );
            if ( $name ) {
                $colors[] = [
                    'name'        => $name,
                    'image_index' => max( 0, (int) ( $item['image_index'] ?? 0 ) ),
                ];
            }
        }
        return $colors;
    }

    // -------------------------------------------------------------------------
    // Shared JSON decode — 3-attempt with fallbacks
    // -------------------------------------------------------------------------

    private static function decode( string $raw ): array|WP_Error {
        $clean = preg_replace( '/^```(?:json)?\s*/i', '', trim( $raw ) );
        $clean = preg_replace( '/\s*```$/i', '', $clean );

        // Attempt 1: direct decode.
        $data = json_decode( $clean, true );

        // Attempt 2: extract outermost JSON object.
        $extracted = null;
        if ( json_last_error() !== JSON_ERROR_NONE ) {
            $start = strpos( $clean, '{' );
            $end   = strrpos( $clean, '}' );
            if ( $start !== false && $end !== false && $end > $start ) {
                $extracted = substr( $clean, $start, $end - $start + 1 );
                $data      = json_decode( $extracted, true );
            }
        }

        // Attempt 3: strip literal control characters.
        if ( json_last_error() !== JSON_ERROR_NONE ) {
            $target    = $extracted ?? $clean;
            $sanitized = preg_replace( '/[\x00-\x1F\x7F]/', ' ', $target );
            if ( $sanitized ) {
                $data = json_decode( $sanitized, true );
            }
        }

        if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $data ) ) {
            return new WP_Error(
                'pipebu_parse',
                /* translators: %s: error message from JSON parser */
                sprintf( __( 'Could not parse AI response: %s', 'pipeline-buster' ), json_last_error_msg() )
            );
        }

        return $data;
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    private static function ids_to_paths( array $ids ): array|WP_Error {
        $paths = [];
        foreach ( $ids as $id ) {
            $path = get_attached_file( (int) $id );
            if ( ! $path || ! file_exists( $path ) ) {
                /* translators: %d: WordPress attachment ID */
                return new WP_Error( 'pb_missing', sprintf( __( 'Attachment #%d not found.', 'pipeline-buster' ), $id ) );
            }
            $paths[] = $path;
        }
        if ( empty( $paths ) ) {
            return new WP_Error( 'pb_no_images', __( 'No images provided.', 'pipeline-buster' ) );
        }
        return $paths;
    }
}
