<?php

namespace App\Services;

use App\Models\Product;
use App\Models\Setting;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class BadhonsworldService
{
    /**
     * Cache for badhonsworld category names indexed by their MongoDB Object ID.
     */
    protected array $categoriesCache = [];

    /**
     * Fetch all categories from badhonsworld API.
     */
    public function fetchBadhonsCategories(): array
    {
        try {
            $response = Http::withHeaders($this->getHeaders())
                ->timeout(15)
                ->get('https://api.badhonsworld.com/api/v1/category/all');
            if ($response->successful()) {
                $json = $response->json();
                $categories = [];
                if (!empty($json['data']) && is_array($json['data'])) {
                    foreach ($json['data'] as $cat) {
                        $id = $cat['_id'] ?? null;
                        $name = $cat['name']['en'] ?? $cat['name']['bn'] ?? null;
                        if ($id && $name) {
                            $categories[$id] = trim($name);
                        }
                    }
                }
                return $categories;
            }
        } catch (\Exception $e) {
            Log::warning("Failed to fetch badhonsworld categories: " . $e->getMessage());
        }
        return [];
    }

    /**
     * Get the categories cache.
     */
    public function getCategoryCache(): array
    {
        if (empty($this->categoriesCache)) {
            $this->categoriesCache = $this->fetchBadhonsCategories();
        }
        return $this->categoriesCache;
    }

    /**
     * Get API request headers including the reseller authorization token if configured.
     */
    public function getHeaders(): array
    {
        $token = Setting::getValue('badhonsworld_reseller_token');
        $headers = [
            'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        ];
        if (!empty($token)) {
            $headers['authorization'] = trim($token);
        }
        return $headers;
    }

    /**
     * Resolve cloudfront CDN image URL.
     */
    public function resolveImageUrl(?string $path): ?string
    {
        if (empty($path)) {
            return null;
        }
        if (str_starts_with($path, 'http://') || str_starts_with($path, 'https://')) {
            return $path;
        }
        return 'https://d2wuw2wo2jxqrg.cloudfront.net/' . ltrim($path, '/');
    }

    /**
     * Build SEO-optimized slug by filtering out stop words and emojis.
     */
    public function generateSeoSlug(string $name): string
    {
        // Strip emojis
        $cleanName = preg_replace('/[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F800}-\x{1F8FF}\x{1F900}-\x{1F9FF}\x{1FA00}-\x{1FA6F}\x{1FA70}-\x{1FAFF}\x{2700}-\x{27BF}\x{2600}-\x{26FF}\x{2300}-\x{23FF}\x{2B50}\x{2B06}\x{2192}\x{2934}\x{25AA}\x{25FE}]/u', '', $name);
        
        $stopWords = [
            'a', 'an', 'the', 'and', 'or', 'of', 'with', 'for', 'in', 'on', 'at', 'to', 'by', 'from', 'is', 'are', 'was', 'were', 'amp',
            'এবং', 'ও', 'আর', 'জন্য', 'থেকে', 'পর', 'প্রতি', 'দিয়ে', 'দ্বারা'
        ];
        $words = preg_split('/\s+/', $cleanName);
        $filteredWords = array_filter($words, function ($word) use ($stopWords) {
            return !empty($word) && !in_array(mb_strtolower($word), $stopWords);
        });
        $slug = Str::slug(implode(' ', $filteredWords));
        if (empty($slug)) {
            $slug = Str::slug($name);
        }

        // Ensure slug is unique
        $originalSlug = $slug;
        $counter = 1;
        while (Product::where('slug', $slug)->exists()) {
            $slug = $originalSlug . '-' . $counter;
            $counter++;
        }
        return $slug;
    }

    /**
     * Import or update a single product from raw API data.
     */
    public function importOrUpdateProduct(array $productData, int $categoryId): ?Product
    {
        $productId = $productData['_id'] ?? null;
        if (!$productId) {
            return null;
        }

        $name = $this->replaceBrandName(trim($productData['product_name'] ?? ''));
        if (empty($name)) {
            return null;
        }

        // Prices calculation
        $originalPrice = floatval($productData['price'] ?? 0);
        $discountAmount = floatval($productData['discount'] ?? 0);
        $price = $originalPrice - $discountAmount;
        $compareAtPrice = ($discountAmount > 0) ? $originalPrice : null;
        $costPrice = floatval($productData['seller_price'] ?? 0);
        $totalQty = intval($productData['total_quantity'] ?? 0);

        // Find existing product or create new
        $product = Product::where('external_id', $productId)->first();

        // Resolve product category using badhonsworld categories
        $badhonsCategoryId = $productData['category'] ?? null;
        $matchedCategoryId = $categoryId; // fallback to default

        if ($badhonsCategoryId) {
            $cache = $this->getCategoryCache();
            if (isset($cache[$badhonsCategoryId])) {
                $catName = $cache[$badhonsCategoryId];
                
                // Find or create local category
                $localCategory = \App\Models\Category::where('name', $catName)->first();
                if (!$localCategory) {
                    $localCategory = \App\Models\Category::create([
                        'name' => $catName,
                        'slug' => Str::slug($catName),
                    ]);
                }
                $matchedCategoryId = $localCategory->id;
            }
        }

        if ($product) {
            // Update prices, description, and stock (also update category in case it was changed)
            $product->update([
                'category_id' => $matchedCategoryId,
                'price' => $price,
                'compare_at_price' => $compareAtPrice,
                'cost_price' => $costPrice,
                'quantity' => $totalQty,
                'name' => $name,
                'description' => $this->cleanHtmlSpacing($this->replaceBrandName($productData['description'] ?? null)),
            ]);
        } else {
            // Create brand-new product
            $slug = $this->generateSeoSlug($name);
            $product = Product::create([
                'category_id' => $matchedCategoryId,
                'name' => $name,
                'slug' => $slug,
                'description' => $this->cleanHtmlSpacing($this->replaceBrandName($productData['description'] ?? null)),
                'sku' => Product::generateUniqueSku($name),
                'price' => $price,
                'compare_at_price' => $compareAtPrice,
                'cost_price' => $costPrice,
                'quantity' => $totalQty,
                'is_visible' => true,
                'is_featured' => false,
                'published_at' => now(),
                'external_id' => $productId,
            ]);

            // Download images concurrently (only for new products to prevent slow downs on update)
            $imageUrls = [];
            $resolvedThumbnail = $this->resolveImageUrl($productData['thumbnail'] ?? null);
            if ($resolvedThumbnail) {
                $imageUrls['thumbnail'] = $resolvedThumbnail;
            }
            
            if (!empty($productData['product_slider_images']) && is_array($productData['product_slider_images'])) {
                foreach ($productData['product_slider_images'] as $index => $sliderImage) {
                    $resolvedSlider = $this->resolveImageUrl($sliderImage);
                    if ($resolvedSlider) {
                        $imageUrls['slider_' . $index] = $resolvedSlider;
                    }
                }
            }

            if (!empty($imageUrls)) {
                $activeDisk = config('filesystems.default', 'public');
                foreach ($imageUrls as $key => $url) {
                    $this->downloadAndAttachImage($product, $url, $activeDisk);
                }
            }

            // Create variants
            if (!empty($productData['colors']) && is_array($productData['colors'])) {
                foreach ($productData['colors'] as $colorData) {
                    if (!empty($colorData['color'])) {
                        try {
                            $product->variants()->create([
                                'sku' => $product->sku . '-' . strtoupper(Str::slug($colorData['color'])),
                                'price' => $price,
                                'quantity' => intval($colorData['available_quantity'] ?? 0),
                                'options' => [
                                    'Color' => $colorData['color']
                                ]
                            ]);
                        } catch (\Exception $e) {
                            Log::warning("Failed to create color variant for product {$product->id}: " . $e->getMessage());
                        }
                    }
                }
            }
        }

        return $product;
    }

    /**
     * Run full paginated bulk import of all products.
     */
    public function bulkImportAll(int $defaultCategoryId, ?callable $onProgress = null): int
    {
        $limit = 100;
        $page = 1;
        $importedCount = 0;

        while (true) {
            $apiUrl = "https://api.badhonsworld.com/api/v1/product?limit={$limit}&page={$page}";
            
            try {
                $response = Http::withHeaders($this->getHeaders())
                    ->timeout(30)
                    ->get($apiUrl);
            } catch (\Exception $e) {
                Log::error("Bulk Import: API Connection failed on page {$page}: " . $e->getMessage());
                break;
            }

            if (!$response->successful()) {
                Log::error("Bulk Import: API returned error {$response->status()} on page {$page}");
                break;
            }

            $json = $response->json();
            $productsList = $json['data']['products'] ?? [];
            if (empty($productsList)) {
                break; // No more products
            }

            foreach ($productsList as $item) {
                $productId = $item['_id'] ?? null;
                if (!$productId) {
                    continue;
                }

                // Call product-details endpoint to get full attributes (description, colors, gallery slider)
                $detailsUrl = "https://api.badhonsworld.com/api/v1/product/product-details?id={$productId}";
                try {
                    $detailsResponse = Http::withHeaders($this->getHeaders())
                        ->timeout(15)
                        ->get($detailsUrl);
                        
                    if ($detailsResponse->successful()) {
                        $detailsJson = $detailsResponse->json();
                        if (!empty($detailsJson['data'])) {
                            $product = $this->importOrUpdateProduct($detailsJson['data'], $defaultCategoryId);
                            if ($product) {
                                $importedCount++;
                            }
                        }
                    }
                } catch (\Exception $e) {
                    Log::warning("Bulk Import: Failed to fetch details for {$productId}: " . $e->getMessage());
                }

                if ($onProgress) {
                    $onProgress($importedCount);
                }
            }

            $page++;
        }

        return $importedCount;
    }

    /**
     * Sync stock levels of all products in local database that have an external_id.
     */
    public function syncAllProductStock(): int
    {
        $limit = 100;
        $page = 1;
        $syncedCount = 0;
        $apiProductStocks = [];

        // 1. Fetch all stock levels from badhonsworld API page-by-page
        while (true) {
            $apiUrl = "https://api.badhonsworld.com/api/v1/product?limit={$limit}&page={$page}";
            
            try {
                $response = Http::withHeaders($this->getHeaders())
                    ->timeout(30)
                    ->get($apiUrl);
            } catch (\Exception $e) {
                Log::error("Stock Sync: API Connection failed on page {$page}: " . $e->getMessage());
                break;
            }

            if (!$response->successful()) {
                Log::error("Stock Sync: API returned error {$response->status()} on page {$page}");
                break;
            }

            $json = $response->json();
            $productsList = $json['data']['products'] ?? [];
            if (empty($productsList)) {
                break;
            }

            foreach ($productsList as $item) {
                $productId = $item['_id'] ?? null;
                if ($productId) {
                    // available_quantity represents the real-time stock quantity
                    $apiProductStocks[$productId] = intval($item['available_quantity'] ?? 0);
                }
            }

            $page++;
        }

        // 2. Loop through local products that have external_id and update their stock levels and visibility
        Product::whereNotNull('external_id')
            ->chunk(100, function ($products) use ($apiProductStocks, &$syncedCount) {
                foreach ($products as $product) {
                    if (isset($apiProductStocks[$product->external_id])) {
                        $newStock = $apiProductStocks[$product->external_id];
                        $updates = [];
                        if ($product->quantity != $newStock) {
                            $updates['quantity'] = $newStock;
                        }
                        // If it becomes available again and was hidden, make it visible
                        if ($newStock > 0 && !$product->is_visible) {
                            $updates['is_visible'] = true;
                        }
                        if (!empty($updates)) {
                            $product->update($updates);
                            $syncedCount++;
                        }
                    } else {
                        // Product no longer exists on badhonsworld, mark as out of stock and hide
                        $updates = [];
                        if ($product->quantity > 0) {
                            $updates['quantity'] = 0;
                        }
                        if ($product->is_visible) {
                            $updates['is_visible'] = false;
                        }
                        if (!empty($updates)) {
                            $product->update($updates);
                            $syncedCount++;
                        }
                    }
                }
            });

        return $syncedCount;
    }

    /**
     * Clean up dirty HTML spacing, empty paragraphs, and redundant breaks in descriptions.
     */
    public function cleanHtmlSpacing(?string $html): ?string
    {
        if (empty($html)) {
            return $html;
        }

        // 1. Remove empty paragraphs with absolutely nothing in them
        $html = preg_replace('/<p>\s*<\/p>/i', '', $html);

        // 2. Remove empty paragraphs with only spaces or non-breaking spaces
        $html = preg_replace('/<p>\s*(&nbsp;)*\s*<\/p>/i', '', $html);

        // 3. Remove empty paragraphs with only linebreaks
        $html = preg_replace('/<p>\s*<br\s*\/?>\s*<\/p>/i', '', $html);

        // 4. Remove consecutive line breaks (more than two <br> or <br/> tags in a row)
        $html = preg_replace('/(<br\s*\/?>\s*){2,}/i', '<br>', $html);

        // 5. Trim whitespace around blocks
        $html = trim($html);

        return $html;
    }

    /**
     * Clean and replace foreign brand names with our brand name.
     */
    public function replaceBrandName(?string $text): ?string
    {
        if (empty($text)) {
            return $text;
        }

        $ourBrand = \App\Models\Setting::getValue('site_name', 'Sawdabazar');

        // Define terms to replace case-insensitively
        $patterns = [
            '/MayonsBD/i' => $ourBrand,
            '/Mayons BD/i' => $ourBrand,
            '/badhonsworld/i' => $ourBrand,
            '/badhons world/i' => $ourBrand,
        ];

        return preg_replace(array_keys($patterns), array_values($patterns), $text);
    }

    /**
     * Download an image from any URL and attach it to a product's media collection.
     * Tries multiple strategies including Badhonsworld's own Next.js image proxy.
     */
    public function downloadAndAttachImage(\App\Models\Product $product, string $url, string $disk): bool
    {
        // Build list of URLs to attempt, starting with Badhonsworld's Next.js proxy.
        // The proxy fetches from any source (including ImgBB) on Badhonsworld's server,
        // completely bypassing hotlink protection on the original host.
        $urlsToTry = [];

        // If the URL is not already a Badhonsworld proxy URL, try proxy first
        if (!str_contains($url, 'badhonsworld.com/_next/image')) {
            $urlsToTry[] = [
                'url'  => 'https://badhonsworld.com/_next/image?url=' . urlencode($url) . '&w=1200&q=75',
                'label' => 'Badhonsworld proxy',
            ];
        }

        // Then try the original URL directly with browser-like headers
        $urlsToTry[] = ['url' => $url, 'label' => 'direct (browser headers)'];

        $browserHeaders = [
            'User-Agent'      => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
            'Accept'          => 'image/webp,image/apng,image/*,*/*;q=0.8',
            'Accept-Language' => 'en-US,en;q=0.9',
            'Referer'         => 'https://badhonsworld.com/',
        ];

        foreach ($urlsToTry as $attempt) {
            try {
                $response = Http::withHeaders($browserHeaders)
                    ->timeout(5)
                    ->get($attempt['url']);

                if ($response->successful() && strlen($response->body()) > 100) {
                    $tempFile = tempnam(sys_get_temp_dir(), 'bwi');
                    file_put_contents($tempFile, $response->body());

                    // Determine a clean filename from the original URL (not the proxy URL)
                    $filename = basename(parse_url($url, PHP_URL_PATH)) ?: 'image.jpg';
                    // Ensure a valid extension
                    if (!pathinfo($filename, PATHINFO_EXTENSION)) {
                        $filename .= '.jpg';
                    }

                    $product->addMedia($tempFile)
                        ->usingFileName($filename)
                        ->toMediaCollection('images', $disk);

                    @unlink($tempFile);
                    return true;
                }

                Log::debug("Image download via [{$attempt['label']}] failed | URL: {$attempt['url']} | HTTP: " . $response->status());

            } catch (\Exception $e) {
                Log::debug("Image download via [{$attempt['label']}] exception | " . $e->getMessage());
            }
        }

        // All strategies exhausted
        Log::warning("All download strategies failed for image URL: {$url} (product #{$product->id})");
        return false;
    }
}
