from __future__ import annotations

from typing import Any, Dict, Optional

VARIANT_SKU_DETAIL_QUERY = """
query variantSkuPage($cursor: String) {
  productVariants(first: 250, after: $cursor) {
    nodes {
      id
      sku
      product { id }
    }
    pageInfo { hasNextPage endCursor }
  }
}
"""

PRODUCT_VARIANTS_BULK_UPDATE = """
mutation productVariantsBulkUpdate($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
  productVariantsBulkUpdate(productId: $productId, variants: $variants) {
    productVariants {
      id
      sku
      inventoryItem { sku }
      product { id }
    }
    userErrors { field message }
  }
}
"""


def fetch_remote_variant_map(client, *, max_pages: int = 80) -> Dict[str, Dict[str, Optional[str]]]:
    """Variant SKU → {variant_id, product_id}. First occurrence wins on duplicates."""
    remote: Dict[str, Dict[str, Optional[str]]] = {}
    cursor: Optional[str] = None
    for _ in range(max_pages):
        data = client.graphql(VARIANT_SKU_DETAIL_QUERY, {"cursor": cursor})
        connection = data.get("productVariants") or {}
        for node in connection.get("nodes") or []:
            if not isinstance(node, dict):
                continue
            sku = str(node.get("sku") or "").strip()
            if not sku or sku in remote:
                continue
            product = node.get("product") or {}
            remote[sku] = {
                "variant_id": str(node.get("id") or "").strip() or None,
                "product_id": str(product.get("id") or "").strip() or None,
            }
        page_info = connection.get("pageInfo") or {}
        if not page_info.get("hasNextPage"):
            break
        cursor = page_info.get("endCursor")
    return remote


def rename_variant_sku(
    client,
    *,
    variant_id: str,
    product_id: str,
    new_sku: str,
) -> Dict[str, Any]:
    """Update a Shopify variant SKU only (no product attribute push).

    Uses productVariantsBulkUpdate — productVariantUpdate was removed in API 2024-10+.
    """
    new_sku = str(new_sku or "").strip()
    variant_id = str(variant_id or "").strip()
    product_id = str(product_id or "").strip()
    if not variant_id or not product_id or not new_sku:
        raise ValueError("variant_id, product_id, and new_sku are required")
    data = client.graphql(
        PRODUCT_VARIANTS_BULK_UPDATE,
        {
            "productId": product_id,
            "variants": [
                {
                    "id": variant_id,
                    # SKU lives on InventoryItem in Admin API 2024-10+ (not on variant input).
                    "inventoryItem": {"sku": new_sku},
                }
            ],
        },
    )
    result = data.get("productVariantsBulkUpdate") or {}
    errors = result.get("userErrors") or []
    if errors:
        raise RuntimeError(f"productVariantsBulkUpdate userErrors: {errors}")
    variants = result.get("productVariants") or []
    variant = variants[0] if variants else {}
    inventory_item = variant.get("inventoryItem") or {}
    resolved_sku = inventory_item.get("sku") or variant.get("sku") or new_sku
    return {
        "variant_id": variant.get("id") or variant_id,
        "sku": resolved_sku,
        "product_id": ((variant.get("product") or {}).get("id")) or product_id,
    }
