"""
This module provides functionality to search for products in the Shopify store based on a query string.
It returns product information in a card format suitable for display, along with a conversational response
generated by an LLM to engage with the customer.

Module Name: search_products

This file contains the code for searching products in Shopify.
"""

import inspect
import json

import shopify
from pydantic import BaseModel

from arklex.env.tools.shopify._exception_prompt import ShopifyExceptionPrompt
from arklex.env.tools.shopify.base.entities import ShopifyAdminAuth
from arklex.env.tools.shopify.legacy.utils_nav import cursorify
from arklex.env.tools.shopify.utils.utils import authorify_admin
from arklex.env.tools.shopify.utils.utils_slots import (
    ShopifySearchProductsSlots,
)

# Admin API
from arklex.env.tools.tools import register_tool
from arklex.orchestrator.NLU.services.model_service import ModelService
from arklex.utils.exceptions import ToolExecutionError
from arklex.utils.llm_config import LLMConfig
from arklex.utils.logging_utils import LogContext

log_context = LogContext(__name__)


class SearchProductsOutput(BaseModel):
    response: str


@register_tool(
    description="Search products by string query. If no products are found, the function will return an error message.",
    slots=ShopifySearchProductsSlots.get_all_slots(),
)
def search_products(
    product_query: str, auth: ShopifyAdminAuth, **kwargs: object
) -> SearchProductsOutput:
    """
    Search for products in the Shopify store based on a query string.

    Args:
        product_query (str): The search query string to find products.
        auth (ShopifyAdminAuth): Authentication credentials for the Shopify store.
        **kwargs: Additional keyword arguments for llm configuration.

    Returns:
        SearchProductsOutput: A JSON string containing:
            - answer: A conversational response generated by the LLM
            - card_list: List of product information in card format, including:
                - id: Product ID
                - title: Product title
                - description: Truncated product description
                - link_url: URL to the product page
                - image_url: URL of the product's main image
                - variants: List of product variants with their details

    Raises:
        ToolExecutionError: If no products are found or if there's an error during the search.
    """
    auth = authorify_admin(auth)
    func_name = inspect.currentframe().f_code.co_name
    nav = cursorify(kwargs)

    try:
        with shopify.Session.temp(**auth):
            response = shopify.GraphQL().execute(f"""
                {{
                    products ({nav[0]}, query: "{product_query}") {{
                        nodes {{
                            id
                            title
                            description
                            handle
                            onlineStoreUrl
                            images(first: 1) {{
                                edges {{
                                    node {{
                                        src
                                        altText
                                    }}
                                }}
                            }}
                            variants (first: 3) {{
                                nodes {{
                                    displayName
                                    id
                                    price
                                    inventoryQuantity
                                }}
                            }}
                        }}
                        pageInfo {{
                            endCursor
                            hasNextPage
                            hasPreviousPage
                            startCursor
                        }}
                    }}
                }}
            """)
            products = json.loads(response)["data"]["products"]["nodes"]
            card_list = []
            for product in products:
                product_dict = {
                    "id": product.get("id"),
                    "title": product.get("title"),
                    "description": product.get("description", "None")[:180] + "...",
                    "link_url": product.get("onlineStoreUrl")
                    if product.get("onlineStoreUrl")
                    else f"{auth['domain']}/products/{product.get('handle')}",
                    "image_url": product.get("images", {}).get("edges", [])
                    and product.get("images", {})
                    .get("edges", [])[0]
                    .get("node", {})
                    .get("src", "")
                    or "",
                    "variants": product.get("variants", {}).get("nodes", []),
                }
                card_list.append(product_dict)
            if card_list:
                model_service: ModelService = ModelService(
                    LLMConfig.model_validate(kwargs)
                )
                message = f"You are helping a customer search products based on the query and get results below and those results will be presented using product card format.\n\n{json.dumps(card_list)}\n\nGenerate a response to continue the conversation without explicitly mentioning contents of the search result. Include one or two questions about those products to know the user's preference. Keep the response within 50 words.\nDIRECTLY GIVE THE RESPONSE."
                answer = model_service.get_response(message)
                return SearchProductsOutput(
                    response=json.dumps({"answer": answer, "card_list": card_list})
                )
            else:
                raise ToolExecutionError(
                    func_name,
                    extra_message=ShopifyExceptionPrompt.PRODUCT_SEARCH_ERROR_PROMPT,
                )

    except Exception as e:
        raise ToolExecutionError(
            func_name,
            extra_message=ShopifyExceptionPrompt.PRODUCT_SEARCH_ERROR_PROMPT,
        ) from e
