<?php

namespace AIPT\Api\BulkGenerator\FAQ;

use AIPT\Api\BulkGenerator\Common\AIClientFactory;
use AIPT\Api\BulkGenerator\Common\ProductDataFormatter;
use AIPT\Api\BulkGenerator\FAQ\Core\FAQPromptFactory;
use AIPT\Core\ErrorHandler;
use AIPT\Utils\PromptFormatter;

class FAQGeneratorService {
    public const DEFAULT_FAQ_COUNT = 3;
    public const MAX_FAQ_COUNT = 10;
    public const MIN_FAQ_COUNT = 1;
    private const DEFAULT_FORMAT = 'text';
    private const DEFAULT_MODE = 'both';
    private const DEFAULT_WRITE_MODE = 'append';

    private const SUPPORTED_FORMATS = ['text', 'html'];
    private const SUPPORTED_MODES = ['questions', 'answers', 'both'];
    private const SUPPORTED_WRITE_MODES = ['append', 'replace'];

    /**
     * @var FAQPromptFactory
     */
    private $prompt_factory;

    /**
     * @param FAQPromptFactory|null $prompt_factory Optional factory override for testing/DI.
     */
    public function __construct( ?FAQPromptFactory $prompt_factory = null ) {
        $this->prompt_factory = $prompt_factory ?? new FAQPromptFactory();
    }

    /**
     * Generate FAQs for a set of products.
     *
     * @param array $product_ids
     * @param array $options
     * @return array
     */
    public function bulk_generate(array $product_ids, array $options): array {
        $results = [];

        $faq_count = $this->normalize_faq_count($options['faqCount'] ?? self::DEFAULT_FAQ_COUNT);
        $format = $this->normalize_format($options['faqFormat'] ?? self::DEFAULT_FORMAT);
        $mode = $this->normalize_mode($options['faqMode'] ?? self::DEFAULT_MODE);
        $write_mode = $this->normalize_write_mode($options['writeMode'] ?? self::DEFAULT_WRITE_MODE);
        $sources = $this->normalize_sources($options['faqSources'] ?? ['product_description']);

        $language = isset($options['language']) && is_string($options['language'])
            ? sanitize_text_field($options['language'])
            : get_option('aipt_language', 'English');

        $additional_prompt = !empty($options['additionalPrompt']) && is_string($options['additionalPrompt'])
            ? PromptFormatter::cleanContent($options['additionalPrompt'])
            : '';

        $default_faq_options = array(
            'global_additional_prompt' => '',
        );

        /**
         * Filter FAQ generator options.
         *
         * Allows premium module to extend FAQ generation with additional prompt from settings.
         * In free version, global_additional_prompt remains empty string.
         *
         * @param array $default_options Default free options.
         * @param array $options         Options passed from request.
         * @return array Modified options.
         */
        $filtered_faq_options = apply_filters( 'aipt_faq_generator_options', $default_faq_options, $options );

        $global_additional_prompt = isset( $filtered_faq_options['global_additional_prompt'] )
            ? PromptFormatter::cleanContent( $filtered_faq_options['global_additional_prompt'] )
            : '';

        $provider = isset($options['provider']) && is_string($options['provider'])
            ? sanitize_text_field($options['provider'])
            : get_option('aipt_api_provider', 'openai');

        $model = isset($options['model']) && is_string($options['model'])
            ? sanitize_text_field($options['model'])
            : null;

        foreach ($product_ids as $product_id) {
            $product_id = absint($product_id);
            if ($product_id <= 0) {
                continue;
            }

            $product = wc_get_product($product_id);
            if (!$product) {
                $results[] = [
                    'product_id' => $product_id,
                    'error' => __('Product could not be loaded', 'ai-product-tools'),
                ];
                continue;
            }

            try {
                $product_data = ProductDataFormatter::getFormattedProductData($product);
                $prompt_payload = $this->prompt_factory->build_prompt_payload([
                    'format' => $format,
                    'mode' => $mode,
                    'faq_count' => $faq_count,
                    'language' => $language,
                    'write_mode' => $write_mode,
                    'sources' => $sources,
                    'additional_prompt' => $additional_prompt,
                    'global_additional_prompt' => $global_additional_prompt,
                ], $product_data, $product);

                $client = AIClientFactory::create($provider, [
                    'category' => $product_data['categories'] ?? '',
                    'generation_type' => 'faq',
                    'model' => $model,
                ]);

                $raw_output = $client->generate_faq_content(
                    $prompt_payload['system_prompt'],
                    $prompt_payload['user_prompt'],
                    $model
                );

                $parsed = $this->parse_faq_response($raw_output, $format, $mode, $faq_count);

                $results[] = [
                    'product_id' => $product_id,
                    'faqs' => $parsed['faqs'],
                    'format' => $format,
                    'mode' => $mode,
                    'raw' => $parsed['raw'],
                ];
            } catch (\Exception $exception) {
                $results[] = [
                    'product_id' => $product_id,
                    'error' => ErrorHandler::format_api_error_message(ucfirst($provider), $exception->getMessage()),
                ];
            }
        }

        return $results;
    }

    private function normalize_faq_count($count): int {
        $value = is_numeric($count) ? (int) $count : self::DEFAULT_FAQ_COUNT;
        if ($value < self::MIN_FAQ_COUNT) {
            $value = self::MIN_FAQ_COUNT;
        }
        if ($value > self::MAX_FAQ_COUNT) {
            $value = self::MAX_FAQ_COUNT;
        }
        return $value;
    }

    private function normalize_format($format): string {
        $value = is_string($format) ? strtolower($format) : self::DEFAULT_FORMAT;
        return in_array($value, self::SUPPORTED_FORMATS, true) ? $value : self::DEFAULT_FORMAT;
    }

    private function normalize_mode($mode): string {
        $value = is_string($mode) ? strtolower($mode) : self::DEFAULT_MODE;
        return in_array($value, self::SUPPORTED_MODES, true) ? $value : self::DEFAULT_MODE;
    }

    private function normalize_write_mode($write_mode): string {
        $value = is_string($write_mode) ? strtolower($write_mode) : self::DEFAULT_WRITE_MODE;
        return in_array($value, self::SUPPORTED_WRITE_MODES, true) ? $value : self::DEFAULT_WRITE_MODE;
    }

    private function normalize_sources($sources): array {
        $normalized = [];
        if (is_array($sources)) {
            foreach ($sources as $source) {
                $key = sanitize_key($source);
                if (!empty($key)) {
                    $normalized[] = $key;
                }
            }
        }

        if (!in_array('product_description', $normalized, true)) {
            $normalized[] = 'product_description';
        }

        return array_values(array_unique($normalized));
    }

    /**
     * @param string $raw_output
     * @param string $format
     * @param string $mode
     * @param int    $faq_count
     * @return array{faqs:array<int,array<string,string>>,raw:string}
     */
    private function parse_faq_response(string $raw_output, string $format, string $mode, int $faq_count): array {
        $clean_output = $this->strip_code_fences(PromptFormatter::cleanContent($raw_output));

        $faqs = [];

        if ($format === 'text') {
            $faqs = $this->parse_text_output($clean_output, $mode);
        } else {
            $faqs = $this->parse_html_output($clean_output, $mode);
        }

        $faqs = array_slice(
            array_map(
                function (array $faq_item) use ($format): array {
                    return $this->sanitize_faq_item($faq_item, $format);
                },
                $faqs
            ),
            0,
            $faq_count
        );

        return [
            'faqs' => $faqs,
            'raw' => $format === 'html'
                ? wp_kses_post($clean_output)
                : sanitize_textarea_field($clean_output),
        ];
    }

    private function parse_text_output(string $content, string $mode): array {
        $inline_faqs = $this->parse_inline_text_output($content, $mode);
        if (!empty($inline_faqs)) {
            return $inline_faqs;
        }

        $lines = preg_split('/\r\n|\r|\n/', $content);
        $faqs = [];
        $current = ['question' => '', 'answer' => ''];
        $question_pattern = '/^(?:Q(?:uestion)?)(?:\s*\d+)?[\s\.\-:]*\s*(.*)$/i';
        $answer_pattern = '/^(?:A(?:nswer)?)(?:\s*\d+)?[\s\.\-:]*\s*(.*)$/i';

        foreach ($lines as $line) {
            $trimmed = trim($line);
            if ($trimmed === '') {
                if (!empty($current['question']) || !empty($current['answer'])) {
                    $faqs[] = $current;
                    $current = ['question' => '', 'answer' => ''];
                }
                continue;
            }

            if (preg_match($question_pattern, $trimmed, $matches)) {
                if (!empty($current['question']) || !empty($current['answer'])) {
                    $faqs[] = $current;
                    $current = ['question' => '', 'answer' => ''];
                }
                $current['question'] = trim($matches[1]);
                continue;
            }

            if (preg_match($answer_pattern, $trimmed, $matches)) {
                $current['answer'] = trim($matches[1]);
                continue;
            }

            if ($mode === 'answers' && empty($current['answer'])) {
                $current['answer'] = $trimmed;
            } elseif ($mode === 'questions' && empty($current['question'])) {
                $current['question'] = $trimmed;
            } elseif (!empty($current['answer'])) {
                $current['answer'] .= ' ' . $trimmed;
            } else {
                $current['question'] .= (empty($current['question']) ? '' : ' ') . $trimmed;
            }
        }

        if (!empty($current['question']) || !empty($current['answer'])) {
            $faqs[] = $current;
        }

        return $faqs;
    }

    private function parse_inline_text_output(string $content, string $mode): array {
        if (stripos($content, 'A:') === false && stripos($content, 'Answer:') === false) {
            return [];
        }

        $normalized = preg_replace('/\s+/', ' ', trim($content));
        if ($normalized === '') {
            return [];
        }

        $pattern = '/(?:\bQ(?:uestion)?\b\s*\d*(?:[\.\-:])\s*)(.*?)(?:\s*\bA(?:nswer)?\b\s*\d*(?:[\.\-:])\s*)(.*?)(?=\bQ(?:uestion)?\b\s*\d*(?:[\.\-:])|\z)/is';
        preg_match_all($pattern, $normalized, $matches, PREG_SET_ORDER);

        if (empty($matches)) {
            return [];
        }

        $faqs = [];

        foreach ($matches as $match) {
            $question = isset($match[1]) ? trim($match[1]) : '';
            $answer = isset($match[2]) ? trim($match[2]) : '';

            if ($mode === 'questions') {
                $answer = '';
            } elseif ($mode === 'answers') {
                $question = '';
            }

            if ($question === '' && $answer === '') {
                continue;
            }

            $faqs[] = [
                'question' => $question,
                'answer' => $answer,
            ];
        }

        return $faqs;
    }

    private function parse_html_output(string $content, string $mode): array {
        $faqs = [];

        if (preg_match_all('/<dt>(.*?)<\/dt>\s*<dd>(.*?)<\/dd>/is', $content, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $faqs[] = [
                    'question' => wp_strip_all_tags($match[1]),
                    'answer' => wp_strip_all_tags($match[2]),
                ];
            }
            return $faqs;
        }

        if (preg_match_all('/<li>(.*?)<\/li>/is', $content, $matches)) {
            foreach ($matches[1] as $match) {
                if ($mode === 'questions') {
                    $faqs[] = ['question' => wp_strip_all_tags($match), 'answer' => ''];
                    continue;
                }

                $split = preg_split('/\?[\s\-:]*|[:\-]\s*/', wp_strip_all_tags($match), 2);
                if (!empty($split)) {
                    $question = $split[0];
                    $answer = isset($split[1]) ? $split[1] : '';
                    $faqs[] = [
                        'question' => $question,
                        'answer' => $answer,
                    ];
                }
            }
            return $faqs;
        }

        return $this->parse_text_output(wp_strip_all_tags($content), $mode);
    }

    private function sanitize_faq_item(array $faq, string $format): array {
        if ($format === 'html') {
            $question = isset($faq['question']) ? wp_kses_post($faq['question']) : '';
            $answer = isset($faq['answer']) ? wp_kses_post($faq['answer']) : '';
        } else {
            $question = isset($faq['question']) ? sanitize_text_field($faq['question']) : '';
            $answer = isset($faq['answer']) ? sanitize_textarea_field($faq['answer']) : '';
        }

        return [
            'question' => $question,
            'answer' => $answer,
        ];
    }

    private function strip_code_fences(string $content): string {
        $content = trim($content);
        if (preg_match('/^```[a-zA-Z0-9]*\n(.*)\n```$/s', $content, $matches)) {
            return trim($matches[1]);
        }
        return $content;
    }

}
