<?php

namespace App\Services;

use App\Models\Setting;
use Illuminate\Support\Str;
use RuntimeException;

/**
 * Multi-agent product content pipeline (same 5 stages as blog):
 * Research → Write → SEO → Humanize → Images (2 inline only, no featured).
 */
class AiProductGenerator extends AiBlogGenerator
{
    public static function progressCacheKey(string $jobId): string
    {
        return 'ai_product_gen:'.$jobId;
    }

    /** @return list<array{key:string,label:string}> */
    public static function stages(): array
    {
        return [
            ['key' => 'prepare',  'label' => 'آماده‌سازی'],
            ['key' => 'research', 'label' => 'تحقیق محصول'],
            ['key' => 'write',    'label' => 'نگارش توضیحات'],
            ['key' => 'seo',      'label' => 'بهینه‌سازی سئو'],
            ['key' => 'humanize', 'label' => 'انسانی‌سازی متن'],
            ['key' => 'images',   'label' => 'تصاویر داخل توضیحات'],
            ['key' => 'save',     'label' => 'آماده‌سازی خروجی'],
        ];
    }

    /**
     * @return array{
     *   description:string,
     *   features:string,
     *   seo_title:string,
     *   seo_description:string,
     *   seo_keywords:string,
     *   seo_schema:string,
     *   tags:string,
     *   slug:?string,
     *   title_en:?string
     * }
     */
    public function generateForProduct(string $productTitle, ?string $brandName = null, ?string $jobId = null): array
    {
        @set_time_limit(0);
        $this->usedImageUrls = [];

        $cfg    = self::settings();
        $apiKey = GapGptService::apiKey();
        $model  = $cfg['model'];

        if (!$apiKey) {
            throw new RuntimeException('کلید API برای GapGPT تنظیم نشده است. ابتدا توکن را در تنظیمات مدل‌ها ذخیره کنید.');
        }
        if (!$model) {
            throw new RuntimeException('مدل تولید محتوا انتخاب نشده است.');
        }

        $title = $this->sanitize($productTitle);
        if ($title === '') {
            throw new RuntimeException('عنوان محصول خالی است.');
        }

        $keyword = $title;
        $brand   = $this->sanitize((string) $brandName);

        self::reportProgress($jobId, 4, 'prepare', 'آماده‌سازی موضوع و کاتالوگ فروشگاه…');

        $catalog = $cfg['include_catalog']
            ? $this->buildCatalogContext($keyword, $title, (int) $cfg['products_limit'])
            : '';

        self::reportProgress($jobId, 10, 'research', 'ایجنت ۱: در حال تحقیق دربارهٔ محصول…');
        $brief = $this->agentResearchProduct($apiKey, $model, $cfg, $title, $keyword, $brand);

        self::reportProgress($jobId, 28, 'write', 'ایجنت ۲: در حال نگارش توضیحات محصول…');
        $draft = $this->agentWriteProduct($apiKey, $model, $cfg, $title, $keyword, $brand, $brief, $catalog);

        self::reportProgress($jobId, 50, 'seo', 'ایجنت ۳: در حال بهینه‌سازی سئو محصول…');
        $seoBundle = $this->agentSeoProduct($apiKey, $model, $cfg, $draft['description_md'], $draft['features_md'], $title, $keyword, $brand, $brief, $catalog);
        $descMd = $seoBundle['description_markdown'] ?: $draft['description_md'];
        $featMd = $seoBundle['features_markdown'] ?: $draft['features_md'];

        self::reportProgress($jobId, 70, 'humanize', 'ایجنت ۴: در حال انسانی‌سازی متن…');
        $finalDescMd = $this->agentHumanizeProduct($apiKey, $model, $cfg, $descMd, $keyword, 'description');
        $finalFeatMd = $this->agentHumanizeProduct($apiKey, $model, $cfg, $featMd, $keyword, 'features');

        self::reportProgress($jobId, 86, 'images', 'ایجنت ۵: جستجوی ۲ تصویر برای توضیحات…');
        $imagePlan = $this->agentProductImages($apiKey, $model, $cfg, $finalDescMd, $title, $keyword);

        self::reportProgress($jobId, 92, 'images', 'دانلود و درج تصاویر در توضیحات…');
        $html = $this->markdownToHtml($finalDescMd);
        $withImages = $this->applyInlineImagesOnly($html, $imagePlan, $title, $keyword);
        $featuresHtml = $this->sanitizeHtml($this->markdownToHtml($finalFeatMd));

        self::reportProgress($jobId, 96, 'save', 'در حال ساخت اسکیمای Product…');

        $seoTitle = $this->sanitize($seoBundle['seo_title'] ?: $title);
        $seoDesc  = $this->sanitize($seoBundle['meta_description'] ?: mb_substr(strip_tags($withImages['html']), 0, 160));
        $slug     = $this->sanitize($seoBundle['slug'] ?: Str::slug($title) ?: 'product');
        $tags     = is_array($seoBundle['tags'] ?? null)
            ? implode('، ', array_values(array_filter(array_map('strval', $seoBundle['tags']))))
            : $this->sanitize((string) ($seoBundle['tags'] ?? ''));

        $seoSchema = $this->buildProductSchema(
            $title,
            $seoDesc,
            $slug,
            $brand,
            $withImages['image_urls']
        );

        return [
            'description'     => $this->sanitizeHtml($withImages['html']),
            'features'        => $featuresHtml,
            'seo_title'       => $seoTitle,
            'seo_description' => $seoDesc,
            'seo_keywords'    => $this->sanitize($seoBundle['seo_keywords'] ?: $keyword),
            'seo_schema'      => $seoSchema,
            'tags'            => $tags,
            'slug'            => $slug !== '' ? $slug : null,
            'title_en'        => $this->sanitize((string) ($seoBundle['title_en'] ?? '')) ?: null,
        ];
    }

    private function agentResearchProduct(
        string $apiKey,
        string $model,
        array $cfg,
        string $title,
        string $keyword,
        string $brand
    ): string {
        $brandLine = $brand !== '' ? "برند: {$brand}" : 'برند: نامشخص';

        $system = <<<'TXT'
تو یک Product Content Researcher و E-commerce SEO Researcher هستی.
دربارهٔ یک محصول فروشگاهی تحقیق کن و Brief ساختاریافته برای نویسندهٔ صفحه محصول بساز.
اطلاعات را جعل نکن. URL جعلی نساز. متن رقبا را کپی نکن.
اگر به وب زنده دسترسی نداری، بر اساس دانش معتبر بنویس و صریحاً بگو منابع زنده scrape نشده‌اند.
TXT;

        $user = <<<TXT
عنوان محصول: {$title}
{$brandLine}
کلمه کلیدی اصلی: {$keyword}
زبان: {$cfg['language']}
مخاطب: {$cfg['audience']}

خروجی دقیقاً با این ساختار:
1. Search Intent صفحه محصول
2. Primary / Secondary / Semantic Keywords
3. ویژگی‌ها و مشخصات محتمل محصول
4. مزایای خرید و موارد استفاده
5. سوالات پرتکرار خریداران
6. نقاط تمایز پیشنهادی
7. پیشنهاد ساختار توضیحات (H2/H3)
8. پیشنهاد بولت‌های ویژگی‌های اصلی
9. خلاصه اجرایی برای نویسنده
TXT;

        return $this->chat($apiKey, $model, $cfg, $system, $user, 0.4);
    }

    /** @return array{description_md:string,features_md:string} */
    private function agentWriteProduct(
        string $apiKey,
        string $model,
        array $cfg,
        string $title,
        string $keyword,
        string $brand,
        string $brief,
        string $catalog
    ): array {
        $levelMap = [
            'beginner'     => 'مبتدی',
            'intermediate' => 'متوسط',
            'professional' => 'حرفه‌ای',
        ];
        $level = $levelMap[$cfg['audience_level']] ?? 'متوسط';
        $extra = trim((string) $cfg['extra_instructions']);
        $extraBlock = $extra !== '' ? "\nدستورالعمل اضافی ادمین:\n{$extra}\n" : '';
        $catalogBlock = $catalog !== '' ? "\nکاتالوگ فروشگاه برای لینک داخلی واقعی:\n{$catalog}\n" : '';
        $brandLine = $brand !== '' ? "برند: {$brand}" : '';

        $system = <<<'TXT'
تو یک Professional Persian Product Copywriter و E-commerce Content Writer هستی.
برای صفحه محصول، توضیحات کامل و سئو‌شده + بخش کوتاه ویژگی‌های اصلی بنویس.
جملات منابع را کپی نکن. آمار بدون منبع معتبر ننویس.
خروجی فقط یک بلوک JSON در انتهای پاسخ باشد.
TXT;

        $minWords = max(800, (int) $cfg['min_words']);
        $maxWords = max($minWords + 200, (int) $cfg['max_words']);

        $user = <<<TXT
عنوان محصول: {$title}
{$brandLine}
کلمه کلیدی اصلی: {$keyword}
مخاطب: {$cfg['audience']}
سطح: {$level}
طول توضیحات محصول: حدود {$minWords} تا {$maxWords} کلمه (طولانی و کامل)

Research Brief:
{$brief}
{$catalogBlock}{$extraBlock}
قوانین:
- description_markdown: توضیحات کامل محصول با Markdown (مقدمه، H2/H3، مزایا، راهنمای انتخاب/استفاده، FAQ کوتاه، جمع‌بندی). بدون H1.
- features_markdown: توضیح کوتاه ویژگی‌های اصلی (حداکثر حدود ۱۵۰–۲۵۰ کلمه؛ ترجیحاً بولت‌پوینت).
- لینک داخلی فقط از کاتالوگ بالا
- فارسی طبیعی و فروشگاهی، نه شعار توخالی

---JSON---
{
  "description_markdown": "",
  "features_markdown": ""
}
TXT;

        $raw = $this->chat($apiKey, $model, $cfg, $system, $user, (float) $cfg['temperature']);
        $json = $this->extractJsonTrailer($raw);

        $desc = trim((string) ($json['description_markdown'] ?? ''));
        $feat = trim((string) ($json['features_markdown'] ?? ''));

        if ($desc === '') {
            $desc = $this->extractMarkdownFallback($raw);
        }
        if ($feat === '') {
            $feat = "- مناسب برای استفاده روزمره\n- کیفیت ساخت مطمئن\n- انتخابی کاربردی برای خریداران";
        }

        return [
            'description_md' => $desc,
            'features_md'    => $feat,
        ];
    }

    /** @return array<string, mixed> */
    private function agentSeoProduct(
        string $apiKey,
        string $model,
        array $cfg,
        string $descriptionMd,
        string $featuresMd,
        string $title,
        string $keyword,
        string $brand,
        string $brief,
        string $catalog
    ): array {
        $brandLine = $brand !== '' ? "برند: {$brand}" : '';
        $catalogBlock = $catalog !== '' ? "\nکاتالوگ فروشگاه:\n{$catalog}\n" : '';

        $system = <<<'TXT'
تو یک Senior Technical SEO Specialist برای صفحات محصول فروشگاهی هستی.
این صفحه Product است نه BlogPost.
اسکیما/JSON-LD نساز (سیستم خودش Product schema می‌سازد).
URL جعلی نساز. Keyword Stuffing نکن.
در پایان دقیقاً یک بلوک JSON با ---JSON--- بده.
TXT;

        $user = <<<TXT
عنوان محصول: {$title}
{$brandLine}
کلمه کلیدی اصلی: {$keyword}
طول Title: {$cfg['seo_title_min']}-{$cfg['seo_title_max']} کاراکتر
طول Meta Description: {$cfg['seo_desc_min']}-{$cfg['seo_desc_max']} کاراکتر

Brief:
{$brief}

توضیحات فعلی:
{$descriptionMd}

ویژگی‌های اصلی فعلی:
{$featuresMd}
{$catalogBlock}
خروجی متنی کوتاه شامل SEO Audit و Improvements، سپس این JSON:
---JSON---
{
  "seo_title": "",
  "meta_description": "",
  "slug": "",
  "title_en": "",
  "seo_keywords": "kw1, kw2, kw3",
  "tags": ["تگ1","تگ2"],
  "description_markdown": "توضیحات کامل بهینه‌شده Markdown",
  "features_markdown": "ویژگی‌های اصلی بهینه‌شده Markdown"
}
TXT;

        $raw = $this->chat($apiKey, $model, $cfg, $system, $user, 0.35);
        $json = $this->extractJsonTrailer($raw);

        return [
            'seo_title'             => (string) ($json['seo_title'] ?? ''),
            'meta_description'      => (string) ($json['meta_description'] ?? ''),
            'slug'                  => (string) ($json['slug'] ?? ''),
            'title_en'              => (string) ($json['title_en'] ?? ''),
            'seo_keywords'          => (string) ($json['seo_keywords'] ?? $keyword),
            'tags'                  => is_array($json['tags'] ?? null) ? array_values(array_filter(array_map('strval', $json['tags']))) : [],
            'description_markdown'  => trim((string) ($json['description_markdown'] ?? '')) ?: $descriptionMd,
            'features_markdown'     => trim((string) ($json['features_markdown'] ?? '')) ?: $featuresMd,
        ];
    }

    private function agentHumanizeProduct(
        string $apiKey,
        string $model,
        array $cfg,
        string $markdown,
        string $keyword,
        string $kind
    ): string {
        $kindLabel = $kind === 'features' ? 'ویژگی‌های اصلی محصول' : 'توضیحات کامل محصول';

        $system = <<<'TXT'
تو یک Senior Persian Readability Editor هستی.
متن فروشگاهی را روان و انسان‌محور کن بدون تغییر معنی یا اختراع اطلاعات جدید.
خروجی فقط Markdown نهایی است.
TXT;

        $user = <<<TXT
نوع متن: {$kindLabel}
کلمه کلیدی: {$keyword}
مخاطب: {$cfg['audience']}

متن:
{$markdown}
TXT;

        $out = trim($this->chat($apiKey, $model, $cfg, $system, $user, 0.45));

        return $out !== '' ? $out : $markdown;
    }

    /**
     * @return array{inline:list<array{search_query:string,alt:string}>}
     */
    private function agentProductImages(
        string $apiKey,
        string $model,
        array $cfg,
        string $descriptionMd,
        string $title,
        string $keyword
    ): array {
        $system = <<<'TXT'
تو یک Visual Content Researcher برای صفحه محصول هستی.
دقیقاً ۲ تصویر برای داخل توضیحات محصول پیشنهاد بده (نه تصویر شاخص گالری).
search_query برای جستجوی عمومی وب؛ alt فارسی طبیعی.
فقط JSON بده.
TXT;

        $user = <<<TXT
عنوان محصول: {$title}
کلمه کلیدی: {$keyword}
مخاطب: {$cfg['audience']}

خلاصه توضیحات:
{$descriptionMd}

---JSON---
{
  "inline": [
    {"search_query": "", "alt": ""},
    {"search_query": "", "alt": ""}
  ]
}
TXT;

        $raw = $this->chat($apiKey, $model, $cfg, $system, $user, 0.4);
        $json = $this->extractJsonTrailer($raw);
        $inlineRaw = data_get($json, 'inline', []);
        $inline = [];

        if (is_array($inlineRaw)) {
            foreach ($inlineRaw as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $q = trim((string) ($row['search_query'] ?? ''));
                $a = trim((string) ($row['alt'] ?? ''));
                if ($q === '' && $a === '') {
                    continue;
                }
                $inline[] = [
                    'search_query' => $q !== '' ? $q : $keyword,
                    'alt'          => $a !== '' ? $a : $title,
                ];
                if (count($inline) >= 2) {
                    break;
                }
            }
        }

        while (count($inline) < 2) {
            $n = count($inline) + 1;
            $inline[] = [
                'search_query' => trim($keyword.' product photo '.$n) ?: ('product '.$n),
                'alt'          => "تصویر مرتبط با {$title}",
            ];
        }

        return ['inline' => array_slice($inline, 0, 2)];
    }

    /**
     * @param  array{inline:list<array{search_query:string,alt:string}>}  $plan
     * @return array{html:string,image_urls:list<string>}
     */
    private function applyInlineImagesOnly(string $html, array $plan, string $title, string $keyword): array
    {
        $inlineBlocks = [];
        $imageUrls = [];

        foreach (array_slice($plan['inline'] ?? [], 0, 2) as $i => $item) {
            $file = $this->findAndStoreImage(
                (string) ($item['search_query'] ?? $keyword),
                public_path('uploads/products/content'),
                'prod_inline'.($i + 1)
            );
            if (!$file) {
                continue;
            }
            $alt = $this->sanitize((string) ($item['alt'] ?? $title));
            $src = asset('uploads/products/content/'.$file);
            $imageUrls[] = $src;
            $inlineBlocks[] = $this->centeredImageHtml($src, $alt);
        }

        if ($inlineBlocks !== []) {
            $html = $this->insertCenteredImages($html, $inlineBlocks);
        }

        return [
            'html'       => $html,
            'image_urls' => $imageUrls,
        ];
    }

    /** @param list<string> $imageUrls */
    private function buildProductSchema(
        string $name,
        string $description,
        string $slug,
        string $brandName,
        array $imageUrls
    ): string {
        $siteName = (string) Setting::get('site_name', config('app.name', 'بیاتوشاپ'));
        $productUrl = route('product.show', $slug !== '' ? $slug : Str::slug($name));

        $data = [
            '@context'    => 'https://schema.org',
            '@type'       => 'Product',
            'name'        => $name,
            'description' => $description,
            'url'         => $productUrl,
            'inLanguage'  => 'fa-IR',
            'offers'      => [
                '@type'         => 'Offer',
                'url'           => $productUrl,
                'priceCurrency' => 'IRR',
                'availability'  => 'https://schema.org/InStock',
                'seller'        => [
                    '@type' => 'Organization',
                    'name'  => $siteName,
                ],
            ],
        ];

        if ($imageUrls !== []) {
            $data['image'] = array_values($imageUrls);
        }

        if ($brandName !== '') {
            $data['brand'] = [
                '@type' => 'Brand',
                'name'  => $brandName,
            ];
        }

        return (string) json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
    }

    private function extractMarkdownFallback(string $raw): string
    {
        $raw = trim($raw);
        if (preg_match('/---JSON---/u', $raw)) {
            $raw = trim((string) preg_replace('/---JSON---.*$/su', '', $raw));
        }
        $raw = preg_replace('/^```(?:markdown|md|json)?\s*/i', '', $raw) ?? $raw;
        $raw = preg_replace('/```\s*$/', '', $raw) ?? $raw;

        return trim($raw);
    }

    /** Quill-friendly centered image (no figure wrapper). */
    protected function centeredImageHtml(string $src, string $alt): string
    {
        $safeSrc = htmlspecialchars($src, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
        $safeAlt = htmlspecialchars($alt, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

        return '<p class="ai-inline-image" style="text-align:center;margin:1.75rem 0;">'
            .'<img src="'.$safeSrc.'" alt="'.$safeAlt.'" '
            .'style="display:inline-block;max-width:100%;height:auto;border-radius:8px;" />'
            .'</p>';
    }
}
