<?php

declare(strict_types=1);

namespace App\Services\AiAssistant;

use App\Services\AiAssistant\Tools\CategoryTools;
use App\Services\AiAssistant\Tools\DealTools;
use App\Services\AiAssistant\Tools\DiscoveryTools;
use App\Services\AiAssistant\Tools\DoWeHaveItTool;
use App\Services\AiAssistant\Tools\ProductTools;
use App\Services\AiAssistant\Tools\RecommendationTools;
use App\Services\AiAssistant\Tools\SearchTools;
use App\Services\AiAssistant\Tools\ShoppingHelperTools;
use Illuminate\Support\Facades\Auth;
use Prism\Prism\Facades\Tool;
use Prism\Prism\Schema\NumberSchema;
use Prism\Prism\Tool as BaseTool;

class ToolManager
{
    protected array $tools = [];

    /** @var array<string, array{text: string, html: string|null, metadata: array|null}> */
    protected array $lastToolResults = [];

    protected ?int $conversationId = null;

    protected ?int $userId = null;

    public function __construct(
        protected ProductTools $productTools,
        protected CategoryTools $categoryTools,
        protected DealTools $dealTools,
        protected RecommendationTools $recommendationTools,
        protected ShoppingHelperTools $shoppingHelperTools,
        protected DiscoveryTools $discoveryTools,
        protected DoWeHaveItTool $doWeHaveItTool,
        protected SearchTools $searchTools,
    ) {
        $this->registerTools();
    }

    /**
     * Set the conversation context for tools.
     */
    public function setContext(?int $conversationId, ?int $userId = null): self
    {
        $this->conversationId = $conversationId;
        $this->userId = $userId ?? Auth::id();

        return $this;
    }

    protected function registerTools(): void
    {
        $this->tools = [
            // Availability check - use FIRST for outside product queries
            $this->doWeHaveItTool(),

            // Smart search (replaces old search_products)
            $this->smartSearchTool(),
            $this->confirmSearchTool(),
            $this->showMoreResultsTool(),

            // Product tools
            $this->getProductDetailsTool(),
            $this->compareProductsTool(),

            // Deal tools
            $this->findDealsTool(),

            // Recommendation tools
            $this->getRecommendationsTool(),

            // Category tools
            $this->getCategoriesTool(),
            $this->getCategoryProductsTool(),

            // Shopping helper tools
            $this->findByPriceRangeTool(),
            $this->getBestSellersTool(),
            $this->getUrgentDealsTool(),
            $this->getLowStockTool(),

            // Discovery tools
            $this->getBudgetFinderTool(),
            $this->getTrendingProductsTool(),
            $this->getSimilarProductsTool(),
            $this->getNewArrivalsTool(),
            $this->getTopRatedTool(),
        ];
    }

    /**
     * @return array<BaseTool>
     */
    public function getAvailableTools(): array
    {
        return $this->tools;
    }

    /**
     * Store a tool result with HTML for later retrieval
     */
    public function storeToolResult(string $toolName, string $argumentsKey, StructuredResponse $response): void
    {
        $key = $toolName.':'.$argumentsKey;
        $this->lastToolResults[$key] = $response->toArray();

        \Log::info('Stored tool result', [
            'tool' => $toolName,
            'args_key' => $argumentsKey,
            'full_key' => $key,
            'has_html' => isset($this->lastToolResults[$key]['html']),
            'html_length' => isset($this->lastToolResults[$key]['html']) ? strlen($this->lastToolResults[$key]['html']) : 0,
            'all_keys' => array_keys($this->lastToolResults),
            'text_preview' => substr($response->text ?? '', 0, 100),
        ]);
    }

    /**
     * Get stored tool result with HTML
     * Removes the result after retrieval to prevent duplicate display
     */
    public function getStoredResult(string $toolName, string $argumentsKey): ?array
    {
        $key = $toolName.':'.$argumentsKey;
        $result = $this->lastToolResults[$key] ?? null;

        // Remove after retrieval to prevent duplicates
        if ($result !== null) {
            unset($this->lastToolResults[$key]);
        }

        return $result;
    }

    /**
     * Get and remove a specific stored result by key
     */
    public function getAndRemoveStoredResult(string $toolName, string $argumentsKey): ?array
    {
        $key = $toolName.':'.$argumentsKey;
        $result = $this->lastToolResults[$key] ?? null;

        if ($result !== null) {
            unset($this->lastToolResults[$key]);
        }

        return $result;
    }

    /**
     * Get all stored results for a specific tool (fallback)
     * Returns results without clearing - caller should use getStoredResult for proper cleanup
     */
    public function getAllStoredForTool(string $toolName): ?array
    {
        $results = [];
        $prefix = $toolName.':';

        foreach ($this->lastToolResults as $key => $value) {
            if (str_starts_with($key, $prefix)) {
                $results[$key] = $value;
            }
        }

        return $results ?: null;
    }

    /**
     * Generate a cache key for tool arguments
     * Normalizes arguments to ensure consistent keys regardless of null handling
     */
    public static function generateArgumentsKey(array $arguments): string
    {
        // Sort keys and filter out null values for consistent key generation
        $normalized = array_filter($arguments, fn ($value) => $value !== null);
        ksort($normalized);

        return md5(json_encode($normalized));
    }

    /**
     * Smart search tool - returns 4 results and stores state for pagination.
     */
    protected function smartSearchTool(): BaseTool
    {
        $manager = $this;
        $searchTools = $this->searchTools;

        return Tool::as('smart_search')
            ->for('Smart product search - shows 4 results at a time. ALWAYS use this when user wants to find products. Works for Arabic: "ابحث عن", "دور لي", "وريني", "شوف", "أبحث", "بحث". After showing results, ask if this is what they want.')
            ->withStringParameter('query', 'Search query (e.g., "phone", "laptop", "موبايل", "لابتوب", "قلم")', required: true)
            ->withNumberParameter('category_id', 'Filter by category ID (optional)', required: false)
            ->withNumberParameter('min_price', 'Minimum price (optional)', required: false)
            ->withNumberParameter('max_price', 'Maximum price (optional)', required: false)
            ->using(function (string $query, ?int $category_id = null, ?float $min_price = null, ?float $max_price = null) use ($manager, $searchTools) {
                $response = $searchTools->smartSearch(
                    compact('query', 'category_id', 'min_price', 'max_price'),
                    $manager->conversationId,
                    $manager->userId
                );

                $argsKey = self::generateArgumentsKey(compact('query', 'category_id', 'min_price', 'max_price'));
                $manager->storeToolResult('smart_search', $argsKey, $response);

                // Include search_state_id in the response so AI can use it for confirmation/more results
                if (isset($response->metadata['search_state_id'])) {
                    return $response->text."\n\n[Search session ID: {$response->metadata['search_state_id']}]";
                }

                return $response->text;
            });
    }

    /**
     * Confirm if search results match user intent.
     */
    protected function confirmSearchTool(): BaseTool
    {
        $manager = $this;
        $searchTools = $this->searchTools;

        return Tool::as('confirm_search_results')
            ->for('Confirm if the search results match what the user is looking for. Call this when user confirms or says "yes/this is what I want"')
            ->withNumberParameter('search_state_id', 'The search session ID from previous search', required: true)
            ->withStringParameter('confirmed', 'User confirmation: "yes" if results match, "no" otherwise', required: true)
            ->using(function (int $search_state_id, string $confirmed) use ($searchTools) {
                $isConfirmed = in_array(strtolower($confirmed), ['yes', 'y', 'true', '1', 'correct', 'right', 'exactly', 'perfect']);
                $response = $searchTools->confirmResults($search_state_id, $isConfirmed);

                return $response->text;
            });
    }

    /**
     * Show more results from previous search.
     */
    protected function showMoreResultsTool(): BaseTool
    {
        $manager = $this;
        $searchTools = $this->searchTools;

        return Tool::as('show_more_results')
            ->for('Show more products from the previous search. Use when user asks for "more", "show more", "next page"')
            ->withNumberParameter('search_state_id', 'The search session ID from previous search', required: true)
            ->using(function (int $search_state_id) use ($manager, $searchTools) {
                $response = $searchTools->showMore($search_state_id);
                $argsKey = self::generateArgumentsKey(['search_state_id' => $search_state_id]);
                $manager->storeToolResult('show_more_results', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getProductDetailsTool(): BaseTool
    {
        $manager = $this;
        $productTools = $this->productTools;

        return Tool::as('get_product_details')
            ->for('Get detailed information about a SPECIFIC product by ID. ONLY use when you have the product ID.')
            ->withNumberParameter('product_id', 'The product ID to get details for (must be a valid numeric ID)', required: true)
            ->using(function (int $product_id) use ($manager, $productTools) {
                $response = $productTools->getDetails($product_id);
                $argsKey = self::generateArgumentsKey(['product_id' => $product_id]);
                $manager->storeToolResult('get_product_details', $argsKey, $response);

                return $response->text;
            });
    }

    protected function compareProductsTool(): BaseTool
    {
        $manager = $this;
        $productTools = $this->productTools;

        return Tool::as('compare_products')
            ->for('Compare multiple products across features, price, ratings, and value')
            ->withArrayParameter('product_ids', 'Array of product IDs to compare (e.g., [1, 2, 3])', new NumberSchema('product_id', 'A product ID'))
            ->using(function (array $product_ids) use ($manager, $productTools) {
                $response = $productTools->compare($product_ids);
                $argsKey = self::generateArgumentsKey(['product_ids' => $product_ids]);
                $manager->storeToolResult('compare_products', $argsKey, $response);

                return $response->text;
            });
    }

    protected function findDealsTool(): BaseTool
    {
        $manager = $this;
        $dealTools = $this->dealTools;

        return Tool::as('find_deals')
            ->for('Find the best deals, discounts, and promotions. Use for: "deals", "discounts", "عروض", "خصومات", "تخفيضات", "العروض"')
            ->withStringParameter('type', 'Type of deals: "best", "flash", "coupon", or "value"', required: false)
            ->withNumberParameter('category_id', 'Filter deals by category ID', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (string $type = 'best', ?int $category_id = null, ?int $limit = 10) use ($manager, $dealTools) {
                $response = $dealTools->find($type, $category_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('type', 'category_id', 'limit'));
                $manager->storeToolResult('find_deals', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getRecommendationsTool(): BaseTool
    {
        $manager = $this;
        $recommendationTools = $this->recommendationTools;

        return Tool::as('get_recommendations')
            ->for('Get product recommendations based on criteria')
            ->withStringParameter('type', 'Type of recommendations: "trending", "similar" (requires product_id), or "personalized" (requires user_id)', required: true)
            ->withNumberParameter('product_id', 'Product ID for "similar" recommendations', required: false)
            ->withNumberParameter('category_id', 'Category ID for category-based recommendations', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (string $type, ?int $product_id = null, ?int $category_id = null, ?int $limit = 10) use ($manager, $recommendationTools) {
                $response = $recommendationTools->get($type, $product_id, $category_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('type', 'product_id', 'category_id', 'limit'));
                $manager->storeToolResult('get_recommendations', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getCategoriesTool(): BaseTool
    {
        $manager = $this;
        $categoryTools = $this->categoryTools;

        return Tool::as('get_categories')
            ->for('List top categories with products (excludes empty categories). Use for: "categories", "show categories", "الأقسام", "الفئات", "وريني الأقسام"')
            ->withStringParameter('search', 'Optional search term to filter categories (leave empty for top categories)', required: false)
            ->withNumberParameter('limit', 'Maximum number of categories to show (default: 10)', required: false)
            ->using(function (string $search = '', ?int $limit = 10) use ($manager, $categoryTools) {
                $response = $categoryTools->list($search, $limit);
                $argsKey = self::generateArgumentsKey(compact('search', 'limit'));
                $manager->storeToolResult('get_categories', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getCategoryProductsTool(): BaseTool
    {
        $manager = $this;
        $categoryTools = $this->categoryTools;

        return Tool::as('get_category_products')
            ->for('Get products in a specific category')
            ->withNumberParameter('category_id', 'The category ID to get products from', required: true)
            ->withStringParameter('sort', 'Sort order: "best_selling", "top_rated", "price_low", "price_high", "most_viewed"', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (int $category_id, string $sort = 'best_selling', ?int $limit = 10) use ($manager, $categoryTools) {
                $response = $categoryTools->getProducts($category_id, $sort, $limit);
                $argsKey = self::generateArgumentsKey(compact('category_id', 'sort', 'limit'));
                $manager->storeToolResult('get_category_products', $argsKey, $response);

                return $response->text;
            });
    }

    protected function findByPriceRangeTool(): BaseTool
    {
        $manager = $this;
        $shoppingHelperTools = $this->shoppingHelperTools;

        return Tool::as('find_by_price_range')
            ->for('Find products within a specific price range')
            ->withNumberParameter('min_price', 'Minimum price', required: true)
            ->withNumberParameter('max_price', 'Maximum price', required: true)
            ->withNumberParameter('category_id', 'Filter by category ID (optional)', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (float $min_price, float $max_price, ?int $category_id = null, ?int $limit = 10) use ($manager, $shoppingHelperTools) {
                $response = $shoppingHelperTools->findByPriceRange($min_price, $max_price, $category_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('min_price', 'max_price', 'category_id', 'limit'));
                $manager->storeToolResult('find_by_price_range', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getBestSellersTool(): BaseTool
    {
        $manager = $this;
        $shoppingHelperTools = $this->shoppingHelperTools;

        return Tool::as('get_best_sellers')
            ->for('Get the best selling products. Use for: "best sellers", "top selling", "الأكثر مبيعا", "الأكثر طلبا"')
            ->withNumberParameter('category_id', 'Filter by category ID (optional)', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (?int $category_id = null, ?int $limit = 10) use ($manager, $shoppingHelperTools) {
                $response = $shoppingHelperTools->getBestSellers($category_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('category_id', 'limit'));
                $manager->storeToolResult('get_best_sellers', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getUrgentDealsTool(): BaseTool
    {
        $manager = $this;
        $shoppingHelperTools = $this->shoppingHelperTools;

        return Tool::as('get_urgent_deals')
            ->for('Get urgent/flash deals with high discounts')
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (?int $limit = 10) use ($manager, $shoppingHelperTools) {
                $response = $shoppingHelperTools->getUrgentDeals($limit);
                $argsKey = self::generateArgumentsKey(['limit' => $limit]);
                $manager->storeToolResult('get_urgent_deals', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getLowStockTool(): BaseTool
    {
        $manager = $this;
        $shoppingHelperTools = $this->shoppingHelperTools;

        return Tool::as('get_low_stock')
            ->for('Get limited quantity offers and low stock items')
            ->withNumberParameter('category_id', 'Filter by category ID (optional)', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (?int $category_id = null, ?int $limit = 10) use ($manager, $shoppingHelperTools) {
                $response = $shoppingHelperTools->getLowStock($category_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('category_id', 'limit'));
                $manager->storeToolResult('get_low_stock', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getBudgetFinderTool(): BaseTool
    {
        $manager = $this;
        $discoveryTools = $this->discoveryTools;

        return Tool::as('get_budget_finder')
            ->for('Find the cheapest/budget products. Use for: "cheapest", "cheap products", "ارخص", "حاجات رخيصة", "اقل سعر", "منتجات اقتصادية"')
            ->withStringParameter('tier', 'Price tier: "budget" (cheapest/under $50), "mid" ($50-$200), "premium" ($200-$1000), or "luxury" ($1000+)', required: true)
            ->withNumberParameter('category_id', 'Filter by category ID (optional)', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (string $tier, ?int $category_id = null, ?int $limit = 10) use ($manager, $discoveryTools) {
                $response = $discoveryTools->getBudgetFinder($tier, $category_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('tier', 'category_id', 'limit'));
                $manager->storeToolResult('get_budget_finder', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getTrendingProductsTool(): BaseTool
    {
        $manager = $this;
        $discoveryTools = $this->discoveryTools;

        return Tool::as('get_trending_products')
            ->for('Get currently trending products based on sales and ratings. Use for: "trending", "popular", "hot", "الأكثر مبيعا", "الرائج", "المطلوب"')
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (?int $limit = 10) use ($manager, $discoveryTools) {
                $response = $discoveryTools->getTrendingProducts($limit);
                $argsKey = self::generateArgumentsKey(['limit' => $limit]);
                $manager->storeToolResult('get_trending_products', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getSimilarProductsTool(): BaseTool
    {
        $manager = $this;
        $discoveryTools = $this->discoveryTools;

        return Tool::as('get_similar_products')
            ->for('Get products similar to a specific product')
            ->withNumberParameter('product_id', 'The product ID to find similar products for', required: true)
            ->withNumberParameter('limit', 'Maximum number of results (default: 5)', required: false)
            ->using(function (int $product_id, ?int $limit = 5) use ($manager, $discoveryTools) {
                $response = $discoveryTools->getSimilarProducts($product_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('product_id', 'limit'));
                $manager->storeToolResult('get_similar_products', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getNewArrivalsTool(): BaseTool
    {
        $manager = $this;
        $discoveryTools = $this->discoveryTools;

        return Tool::as('get_new_arrivals')
            ->for('Get recently added products')
            ->withNumberParameter('category_id', 'Filter by category ID (optional)', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (?int $category_id = null, ?int $limit = 10) use ($manager, $discoveryTools) {
                $response = $discoveryTools->getNewArrivals($category_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('category_id', 'limit'));
                $manager->storeToolResult('get_new_arrivals', $argsKey, $response);

                return $response->text;
            });
    }

    protected function getTopRatedTool(): BaseTool
    {
        $manager = $this;
        $discoveryTools = $this->discoveryTools;

        return Tool::as('get_top_rated')
            ->for('Get top-rated products with 90%+ positive feedback')
            ->withNumberParameter('category_id', 'Filter by category ID (optional)', required: false)
            ->withNumberParameter('limit', 'Maximum number of results (default: 10)', required: false)
            ->using(function (?int $category_id = null, ?int $limit = 10) use ($manager, $discoveryTools) {
                $response = $discoveryTools->getTopRated($category_id, $limit);
                $argsKey = self::generateArgumentsKey(compact('category_id', 'limit'));
                $manager->storeToolResult('get_top_rated', $argsKey, $response);

                return $response->text;
            });
    }

    protected function doWeHaveItTool(): BaseTool
    {
        $manager = $this;
        $doWeHaveItTool = $this->doWeHaveItTool;

        return Tool::as('do_we_have_it')
            ->for('Quickly check if we have products in a specific category or matching a keyword. Use this FIRST when users ask about outside/unusual products.')
            ->withStringParameter('query', 'The product type or category to check (e.g., "laptop", "iphone", "gaming console")', required: true)
            ->using(function (string $query) use ($manager, $doWeHaveItTool) {
                $response = $doWeHaveItTool->check($query);
                $argsKey = self::generateArgumentsKey(['query' => $query]);
                $manager->storeToolResult('do_we_have_it', $argsKey, $response);

                return $response->text;
            });
    }
}
