"""Write Shopify Search & Discovery related/complementary product recommendations."""

from __future__ import annotations

import json
import logging
from typing import Any, Dict, Iterable, List, Optional, Protocol

from db.association_rules import shopify_discovery_buckets

logger = logging.getLogger(__name__)

DISCOVERY_NAMESPACE = "shopify--discovery--product_recommendation"
RELATED_KEY = "related_products"
COMPLEMENTARY_KEY = "complementary_products"
RELATED_DISPLAY_KEY = "related_products_display"

METAFIELDS_SET = """
mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
  metafieldsSet(metafields: $metafields) {
    metafields { id namespace key }
    userErrors { field message code }
  }
}
"""


class ShopifyGraphQLClient(Protocol):
    def graphql(self, query: str, variables: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: ...


def build_discovery_metafields(
    product_id: str,
    associations: Dict[str, Iterable[str]],
    *,
    remote_ids_by_sku: Dict[str, str],
    related_display: str = "ahead",
) -> List[Dict[str, Any]]:
    """Map master associations → Search & Discovery metafield inputs."""
    buckets = shopify_discovery_buckets(
        {k: list(v or []) for k, v in (associations or {}).items()}
    )
    metafields: List[Dict[str, Any]] = []

    related_gids = _sku_list_to_gids(buckets.get("related_products") or [], remote_ids_by_sku)
    complementary_gids = _sku_list_to_gids(
        buckets.get("complementary_products") or [], remote_ids_by_sku
    )

    metafields.append(
        {
            "ownerId": product_id,
            "namespace": DISCOVERY_NAMESPACE,
            "key": RELATED_KEY,
            "type": "list.product_reference",
            "value": json.dumps(related_gids),
        }
    )
    metafields.append(
        {
            "ownerId": product_id,
            "namespace": DISCOVERY_NAMESPACE,
            "key": COMPLEMENTARY_KEY,
            "type": "list.product_reference",
            "value": json.dumps(complementary_gids),
        }
    )
    metafields.append(
        {
            "ownerId": product_id,
            "namespace": DISCOVERY_NAMESPACE,
            "key": RELATED_DISPLAY_KEY,
            "type": "single_line_text_field",
            "value": related_display,
        }
    )
    return metafields


def push_discovery_associations(
    client: ShopifyGraphQLClient,
    *,
    product_id: str,
    associations: Dict[str, Iterable[str]],
    remote_ids_by_sku: Dict[str, str],
) -> Dict[str, Any]:
    metafields = build_discovery_metafields(
        product_id,
        associations,
        remote_ids_by_sku=remote_ids_by_sku,
    )
    data = client.graphql(METAFIELDS_SET, {"metafields": metafields})
    result = data.get("metafieldsSet") or {}
    errors = result.get("userErrors") or []
    if errors:
        raise RuntimeError(f"metafieldsSet userErrors: {errors}")
    return {
        "status": "ok",
        "related": json.loads(metafields[0]["value"]),
        "complementary": json.loads(metafields[1]["value"]),
    }


def _sku_list_to_gids(skus: List[str], remote_ids_by_sku: Dict[str, str]) -> List[str]:
    gids: List[str] = []
    seen = set()
    for sku in skus:
        remote = remote_ids_by_sku.get(sku) or remote_ids_by_sku.get(str(sku).strip())
        if not remote:
            continue
        gid = _as_product_gid(remote)
        if gid in seen:
            continue
        seen.add(gid)
        gids.append(gid)
    return gids


def _as_product_gid(remote_id: str) -> str:
    text = str(remote_id).strip()
    if text.startswith("gid://"):
        return text
    return f"gid://shopify/Product/{text}"
