# Copyright (c) 2025 Microsoft Corporation.
# Licensed under the MIT License

"""Shopify MCP handler for keyword-based product retrieval.

Queries Shopify stores directly via their MCP endpoint
(https://{site}/api/mcp) using the search_shop_catalog tool.
Products are converted to schema.org Product format for ranking.
"""

import json
import logging
from typing import Any

import aiohttp
from nlweb_core.retrieved_item import RetrievedItem

from nlweb_platform_handlers.base import KeywordRetrievalHandler

logger = logging.getLogger(__name__)

# Cache for Shopify MCP results: (site, query) -> list[RetrievedItem]
_shopify_mcp_cache: dict[tuple[str, str], list[RetrievedItem]] = {}


class ShopifyMCPHandler(KeywordRetrievalHandler):
    """Handler for Shopify sites using MCP for retrieval.

    Queries Shopify stores via their MCP endpoint using the
    search_shop_catalog tool. Results are cached per (site, query).

    Configure in config.yaml:
        site_config:
          handler:
            sites:
              mystore.myshopify.com:
                ask_handler_class: ShopifyMCPHandler
                ask_handler_import_path: nlweb_platform_handlers.shopify
    """

    async def _retrieve_items(
        self,
        query: str,
        site: str | None,
        num_results: int,
    ) -> list[RetrievedItem]:
        """Retrieve products from Shopify via MCP."""
        if not site:
            logger.error("No site specified for Shopify MCP search")
            return []

        # Check cache
        cache_key = (site, query)
        if cache_key in _shopify_mcp_cache:
            logger.debug(f"Cache hit for Shopify MCP query: {query} on {site}")
            return _shopify_mcp_cache[cache_key][:num_results]

        endpoint = f"https://{site}/api/mcp"

        mcp_request = {
            "jsonrpc": "2.0",
            "method": "tools/call",
            "params": {
                "name": "search_shop_catalog",
                "arguments": {
                    "query": query,
                    "context": f"User is searching for: {query}",
                    "limit": 50,
                    "country": "US",
                    "language": "EN",
                },
            },
            "id": 1,
        }

        try:
            async with aiohttp.ClientSession() as session:
                async with session.post(
                    endpoint,
                    json=mcp_request,
                    headers={"Content-Type": "application/json"},
                    timeout=aiohttp.ClientTimeout(total=30),
                ) as response:
                    if response.status != 200:
                        logger.error(
                            f"Shopify MCP request failed with status {response.status}"
                        )
                        return []

                    try:
                        result = await response.json(content_type=None)
                    except Exception as e:
                        logger.error(
                            f"Failed to parse Shopify MCP response as JSON: {e}"
                        )
                        return []

                    if "error" in result:
                        logger.error(f"Shopify MCP error: {result['error']}")
                        return []

                    items = self._parse_mcp_response(result, site)
                    _shopify_mcp_cache[cache_key] = items
                    logger.info(
                        f"Shopify MCP query '{query}' to {site}: {len(items)} results"
                    )
                    return items[:num_results]

        except aiohttp.ClientError as e:
            logger.error(f"Shopify MCP request failed: {e}")
            return []
        except Exception as e:
            logger.error(f"Unexpected error in Shopify MCP request: {e}")
            return []

    def _parse_mcp_response(
        self, result: dict[str, Any], site: str
    ) -> list[RetrievedItem]:
        """Parse MCP JSON-RPC response into RetrievedItem list."""
        try:
            mcp_result = result.get("result", {})

            # Handle standard MCP content array format
            if "content" in mcp_result and isinstance(mcp_result["content"], list):
                for content_item in mcp_result["content"]:
                    if content_item.get("type") == "text" and "text" in content_item:
                        try:
                            search_data = json.loads(content_item["text"])
                            return self._format_products(search_data, site)
                        except json.JSONDecodeError:
                            logger.error(
                                "Failed to parse search results from content text"
                            )

            # Try direct format
            return self._format_products(mcp_result, site)

        except Exception as e:
            logger.error(f"Error parsing Shopify MCP response: {e}")
            return []

    def _format_products(
        self, search_data: dict[str, Any], site: str
    ) -> list[RetrievedItem]:
        """Convert Shopify product data to schema.org Product format."""
        retrieved_items: list[RetrievedItem] = []
        products = search_data.get("products", [])

        for product in products:
            schema_object = self._product_to_schema(product)
            url = product.get("url", "")

            retrieved_items.append(
                RetrievedItem(
                    url=url,
                    raw_schema_object=schema_object,
                    site=site,
                )
            )

        logger.info(f"Formatted {len(retrieved_items)} products from Shopify MCP")
        return retrieved_items

    def _product_to_schema(self, product: dict[str, Any]) -> dict[str, Any]:
        """Convert a Shopify product to schema.org Product format."""
        schema_object: dict[str, Any] = {
            "@context": "https://schema.org",
            "@type": "Product",
        }

        field_mappings = {
            "title": "name",
            "product_id": "productID",
            "product_type": "category",
            "image_url": "image",
            "image_alt_text": "imageAltText",
        }

        for key, value in product.items():
            if value is None or value == "":
                continue

            schema_key = field_mappings.get(key, key)

            if key == "vendor" and value:
                schema_object["brand"] = {"@type": "Brand", "name": value}
            elif key == "price_range" and value:
                schema_object["offers"] = {
                    "@type": "AggregateOffer",
                    "priceCurrency": value.get("currency", "USD"),
                    "lowPrice": float(value.get("min") or 0),
                    "highPrice": float(value.get("max") or 0),
                }
            elif key == "variants" and value and len(value) > 1:
                offers_list = []
                for variant in value:
                    offer: dict[str, Any] = {"@type": "Offer"}
                    for v_key, v_value in variant.items():
                        if v_value is not None and v_value != "":
                            offer[v_key] = v_value
                    offers_list.append(offer)
                if offers_list:
                    schema_object["offers"] = offers_list
            elif key not in ("vendor", "price_range", "variants"):
                schema_object[schema_key] = value

        return schema_object
