<?php

declare(strict_types=1);

namespace App\Services\AiAssistant\Tools;

use App\Models\Product;
use App\Services\AiAssistant\AiComponentRenderer;
use App\Services\AiAssistant\StructuredResponse;
use Illuminate\Support\Str;
use Laravel\Scout\Builder;

class ProductTools
{
    public function __construct(
        protected AiComponentRenderer $renderer
    ) {}

    public function search(array $params): StructuredResponse
    {
        $query = $params['query'] ?? '';
        $categoryId = $params['category_id'] ?? null;
        $minPrice = $params['min_price'] ?? null;
        $maxPrice = $params['max_price'] ?? null;
        $limit = min($params['limit'] ?? 10, 50);

        // Check if Scout driver is Meilisearch
        $scoutDriver = config('scout.driver');

        if ($scoutDriver === 'meilisearch' && $query !== '') {
            // Use Meilisearch for full-text search
            $products = $this->searchWithMeilisearch($query, $categoryId, $minPrice, $maxPrice, $limit);
        } else {
            // Fallback to database query (no query or different driver)
            $products = $this->searchWithDatabase($query, $categoryId, $minPrice, $maxPrice, $limit);
        }

        if ($products->isEmpty()) {
            return StructuredResponse::fromText(
                'No products found matching your criteria. Try adjusting your search terms or filters.'
            );
        }

        $text = "Found {$products->count()} product(s):\n\n";

        foreach ($products as $product) {
            $text .= $this->formatProduct($product);
        }

        $html = $this->renderer->productGrid(
            $products,
            "Found {$products->count()} products"
        );

        return StructuredResponse::withHtml($text, $html);
    }

    /**
     * Search using Meilisearch with filters.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    protected function searchWithMeilisearch(
        string $query,
        ?int $categoryId,
        ?float $minPrice,
        ?float $maxPrice,
        int $limit
    ) {
        /** @var Builder $search */
        $search = Product::search($query);

        // Build Meilisearch filter syntax
        // Note: For price filtering, we search broadly then filter by effective price
        // because Meilisearch can't easily filter on COALESCE(discount_price, price)
        $filters = [];

        if ($categoryId !== null) {
            $filters[] = "category_id = {$categoryId}";
        }

        // Use a broader price range for Meilisearch search, then filter by effective price in memory
        // For min_price: search for products where either price OR discount_price meets criteria
        // For max_price: we need to include products where discount_price might be lower than price
        if ($minPrice !== null) {
            // Find products where either original price or discount price is >= minPrice
            $filters[] = "(price >= {$minPrice} OR discount_price >= {$minPrice})";
        }

        if ($maxPrice !== null) {
            // Include products where either price OR discount_price is within range
            // This ensures we catch products with high original price but low discount price
            $filters[] = "(price <= {$maxPrice} OR discount_price <= {$maxPrice})";
        }

        if (! empty($filters)) {
            $search->where(implode(' AND ', $filters));
        }

        // Get raw search results to maintain relevance order
        $rawResults = $search->take($limit * 2)->raw();
        $productIds = collect($rawResults['hits'] ?? [])->pluck('id');

        if ($productIds->isEmpty()) {
            return collect();
        }

        // Fetch full products with relationships, preserving search order
        $products = Product::with('category')
            ->whereIn('id', $productIds)
            ->get()
            ->sortBy(fn ($p) => $productIds->search($p->id))
            ->values();

        // Filter by effective price (discount_price when available, otherwise price)
        $products = $products->filter(function ($product) use ($minPrice, $maxPrice) {
            $effectivePrice = $product->discount_price ?? $product->price;

            if ($minPrice !== null && $effectivePrice < $minPrice) {
                return false;
            }

            if ($maxPrice !== null && $effectivePrice > $maxPrice) {
                return false;
            }

            return true;
        })->values()->take($limit);

        return $products;
    }

    /**
     * Fallback database search when Scout is not configured or no query.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    protected function searchWithDatabase(
        string $query,
        ?int $categoryId,
        ?float $minPrice,
        ?float $maxPrice,
        int $limit
    ) {
        $products = Product::query()
            ->when($query, fn ($q) => $q->where(function ($subQ) use ($query) {
                $subQ->where('name_en', 'like', "%{$query}%")
                    ->orWhere('name_ar', 'like', "%{$query}%")
                    ->orWhere('description_en', 'like', "%{$query}%")
                    ->orWhere('description_ar', 'like', "%{$query}%");
            }))
            ->when($categoryId, fn ($q) => $q->where('category_id', $categoryId))
            ->when($minPrice || $maxPrice, function ($q) use ($minPrice, $maxPrice) {
                // Filter by effective price (discount_price when available, otherwise price)
                // Using raw SQL to check if COALESCE(discount_price, price) falls within range
                if ($minPrice !== null && $maxPrice !== null) {
                    $q->whereRaw('COALESCE(discount_price, price) BETWEEN ? AND ?', [$minPrice, $maxPrice]);
                } elseif ($minPrice !== null) {
                    $q->whereRaw('COALESCE(discount_price, price) >= ?', [$minPrice]);
                } elseif ($maxPrice !== null) {
                    $q->whereRaw('COALESCE(discount_price, price) <= ?', [$maxPrice]);
                }
            })
            ->with('category')
            ->limit($limit)
            ->get();

        return $products;
    }

    public function getDetails(int $productId): StructuredResponse
    {
        $product = Product::with('category')->find($productId);

        if (! $product) {
            return StructuredResponse::fromText("Product with ID {$productId} not found.");
        }

        $text = "**{$product->name_en}**\n\n";
        $text .= '- **Price**: $'.number_format((float) $product->price, 2);

        if ($product->discount_price) {
            $text .= ' (Sale: $'.number_format((float) $product->discount_price, 2).')';
        }

        $text .= "\n";
        $text .= '- **Category**: '.($product->category?->name_en ?? 'N/A')."\n";
        $text .= '- **Rating**: '.$product->positive_feedback."% positive\n";
        $text .= '- **Sales**: '.$product->sales_180day." in last 180 days\n";

        if ($product->discount > 0) {
            $text .= "- **Discount**: {$product->discount}% off\n";
        }

        if ($product->code_name) {
            $text .= "- **Promo Code**: {$product->code_name} (Save $".number_format((float) $product->code_value, 2).")\n";
        }

        if ($product->description_en) {
            $text .= "\n**Description**: ".Str::limit($product->description_en, 200)."\n";
        }

        $html = $this->renderer->productCard($product);

        return StructuredResponse::withHtml($text, $html, [
            'product_id' => $product->id,
            'product_name' => $product->name_en,
        ]);
    }

    public function compare(array $productIds): StructuredResponse
    {
        if (count($productIds) < 2) {
            return StructuredResponse::fromText('Please provide at least 2 product IDs to compare.');
        }

        if (count($productIds) > 5) {
            return StructuredResponse::fromText('I can compare up to 5 products at a time.');
        }

        $products = Product::with('category')
            ->whereIn('id', array_slice($productIds, 0, 5))
            ->get();

        if ($products->count() < 2) {
            return StructuredResponse::fromText('Could not find enough products to compare. Please check the product IDs.');
        }

        $text = "**Comparing {$products->count()} products**\n\n";

        $text .= "| Product | Price | Discount | Rating | Sales | Category |\n";
        $text .= "|---------|-------|----------|--------|-------|----------|\n";

        foreach ($products as $p) {
            $price = $p->discount_price ?? $p->price;
            $discount = $p->discount > 0 ? "{$p->discount}%" : '-';
            $text .= "| {$p->name_en} | $".number_format((float) $price, 2)." | {$discount} | {$p->positive_feedback}% | {$p->sales_180day} | ".($p->category?->name_en ?? '-')." |\n";
        }

        $text .= "\n**Key Insights**:\n\n";

        $bestPrice = $products->sortBy(fn ($p) => $p->discount_price ?? $p->price)->first();
        $text .= "- **Best Price**: {$bestPrice->name_en} at $".number_format((float) ($bestPrice->discount_price ?? $bestPrice->price), 2)."\n";

        $topRated = $products->sortByDesc('positive_feedback')->first();
        $text .= "- **Highest Rated**: {$topRated->name_en} ({$topRated->positive_feedback}% positive)\n";

        $bestDeal = $products->sortByDesc('discount')->first();
        if ($bestDeal->discount > 0) {
            $text .= "- **Best Deal**: {$bestDeal->name_en} ({$bestDeal->discount}% off)\n";
        }

        $html = $this->renderer->comparisonTable($products);

        return StructuredResponse::withHtml($text, $html, [
            'product_count' => $products->count(),
            'product_ids' => $productIds,
        ]);
    }

    protected function formatProduct(Product $product): string
    {
        $line = "- **{$product->name_en}** (ID: {$product->id})\n";
        $line .= '  - Price: $'.number_format((float) $product->price, 2);

        if ($product->discount_price) {
            $line .= ' ~~$'.number_format((float) $product->price, 2)."~~ ({$product->discount}% off)";
        }

        $line .= "\n";
        $line .= "  - Rating: {$product->positive_feedback}% positive";

        if ($product->category) {
            $line .= " | Category: {$product->category->name_en}";
        }

        $line .= "\n";

        if ($product->code_name) {
            $line .= "  - Promo Code: {$product->code_name} (Save $".number_format((float) $product->code_value, 2).")\n";
        }

        $line .= "\n";

        return $line;
    }
}
