"""Shopify data provider implementation.

Fetches data from the Shopify Admin GraphQL API and transforms it
into standard commerce schema. Uses a thin httpx-based async client
rather than the official ShopifyAPI package.

Key design decisions:
    - GraphQL-first (REST is legacy since Oct 2024)
    - Bulk Operations API for full syncs (>1000 records)
    - Cursor pagination for incremental syncs
    - Cost-aware rate limiting (parses throttleStatus from responses)
    - Watermark-based incremental sync (updated_at/created_at filters)

Usage:
    provider = ShopifyProvider(shop="mystore.myshopify.com", token="shpat_xxx")
    result = await provider.fetch("products", since=last_sync_time)
    products: list[Product] = result.records
"""

from __future__ import annotations

import logging
from datetime import datetime
from typing import Any, Dict, List, Optional

from platoon.data.provider import DataProvider, DataProviderResult
from platoon.data.providers.shopify.client import ShopifyGraphQLClient
from platoon.data.providers.shopify.transform import ShopifyTransformer

logger = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# GraphQL query templates
# ---------------------------------------------------------------------------

PRODUCTS_QUERY = """
query Products($first: Int!, $cursor: String, $query: String) {
  products(first: $first, after: $cursor, query: $query) {
    edges {
      node {
        id
        title
        descriptionHtml
        vendor
        productType
        tags
        status
        createdAt
        updatedAt
        variants(first: 100) {
          edges {
            node {
              id
              sku
              title
              price
              compareAtPrice
              inventoryQuantity
              weight
              barcode
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
"""

ORDERS_QUERY = """
query Orders($first: Int!, $cursor: String, $query: String) {
  orders(first: $first, after: $cursor, query: $query) {
    edges {
      node {
        id
        name
        email
        displayFinancialStatus
        displayFulfillmentStatus
        currencyCode
        createdAt
        updatedAt
        totalPriceSet { shopMoney { amount currencyCode } }
        subtotalPriceSet { shopMoney { amount currencyCode } }
        totalTaxSet { shopMoney { amount currencyCode } }
        totalDiscountsSet { shopMoney { amount currencyCode } }
        customer { id }
        lineItems(first: 50) {
          edges {
            node {
              id
              title
              sku
              quantity
              originalUnitPriceSet { shopMoney { amount currencyCode } }
              totalDiscountSet { shopMoney { amount currencyCode } }
              product { id }
              variant { id }
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
"""

CUSTOMERS_QUERY = """
query Customers($first: Int!, $cursor: String, $query: String) {
  customers(first: $first, after: $cursor, query: $query) {
    edges {
      node {
        id
        email
        firstName
        lastName
        numberOfOrders
        amountSpent { amount currencyCode }
        tags
        createdAt
        updatedAt
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
"""

INVENTORY_QUERY = """
query InventoryItems($first: Int!, $cursor: String, $query: String) {
  inventoryItems(first: $first, after: $cursor, query: $query) {
    edges {
      node {
        id
        sku
        unitCost { amount currencyCode }
        tracked
        createdAt
        updatedAt
        variant { id }
        inventoryLevels(first: 10) {
          edges {
            node {
              id
              location { id name }
              quantities(names: ["available", "on_hand", "incoming", "committed", "reserved"]) {
                name
                quantity
              }
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
"""

# Bulk operation queries (no pagination, no variables)
PRODUCTS_BULK_QUERY = """
{
  products {
    edges {
      node {
        id
        title
        descriptionHtml
        vendor
        productType
        tags
        status
        createdAt
        updatedAt
        variants {
          edges {
            node {
              id
              sku
              title
              price
              compareAtPrice
              inventoryQuantity
              weight
              barcode
            }
          }
        }
      }
    }
  }
}
"""


class ShopifyProvider(DataProvider):
    """Shopify integration — fetches commerce data via GraphQL Admin API.

    Supports incremental sync using updated_at/created_at watermarks
    and full sync via Shopify Bulk Operations API.
    """

    name = "shopify"
    entity_types = ["products", "orders", "customers", "inventory"]

    def __init__(
        self,
        shop: str,
        token: str,
        api_version: str = "2026-01",
        page_size: int = 50,
    ):
        """Initialize Shopify provider.

        Args:
            shop: Shopify store domain (e.g. "mystore.myshopify.com")
            token: Admin API access token (shpat_xxx)
            api_version: Shopify API version
            page_size: Records per page for paginated queries
        """
        self.client = ShopifyGraphQLClient(shop=shop, token=token, version=api_version)
        self.transformer = ShopifyTransformer()
        self.page_size = page_size

    async def fetch(
        self,
        entity_type: str,
        since: Optional[datetime] = None,
        config: Optional[Dict[str, Any]] = None,
    ) -> DataProviderResult:
        """Fetch records from Shopify, transform to standard schema.

        Routes to entity-specific fetch methods. Uses cursor pagination
        for incremental syncs, bulk operations for full syncs.

        Args:
            entity_type: "products" | "orders" | "customers" | "inventory"
            since: Fetch records updated/created after this time. None = full sync.
            config: Optional overrides (e.g. {"use_bulk": True})
        """
        fetchers = {
            "products": self._fetch_products,
            "orders": self._fetch_orders,
            "customers": self._fetch_customers,
            "inventory": self._fetch_inventory,
        }
        if entity_type not in fetchers:
            raise ValueError(f"Unknown entity type: {entity_type}. Supported: {list(fetchers.keys())}")

        return await fetchers[entity_type](since=since, config=config or {})

    async def check_connection(self) -> bool:
        """Verify we can reach the Shopify store's GraphQL endpoint."""
        try:
            result = await self.client.execute("{ shop { name } }")
            return "data" in result and "shop" in result.get("data", {})
        except Exception as e:
            logger.warning(f"Shopify connection check failed: {e}")
            return False

    async def close(self) -> None:
        """Close the underlying HTTP client."""
        await self.client.close()

    # ----- Entity-specific fetch methods -----

    async def _fetch_products(
        self, since: Optional[datetime] = None, config: dict = {}
    ) -> DataProviderResult:
        """Fetch products and variants from Shopify."""
        use_bulk = config.get("use_bulk", False)

        if use_bulk and since is None:
            raw = await self.client.bulk_query(PRODUCTS_BULK_QUERY)
        else:
            query_filter = f"updated_at:>'{since.isoformat()}'" if since else None
            raw = await self.client.paginated_query(
                PRODUCTS_QUERY,
                variables={"first": self.page_size, "query": query_filter},
                connection_path=["products"],
            )

        records = self.transformer.products(raw)
        watermark = _max_datetime(records, "updated_at") or since

        return DataProviderResult(
            records=records,
            watermark=watermark,
            entity_type="products",
            raw_count=len(raw),
        )

    async def _fetch_orders(
        self, since: Optional[datetime] = None, config: dict = {}
    ) -> DataProviderResult:
        """Fetch orders and line items from Shopify.

        Orders use created_at as watermark (orders are immutable after creation).
        """
        query_filter = f"created_at:>'{since.isoformat()}'" if since else None
        raw = await self.client.paginated_query(
            ORDERS_QUERY,
            variables={"first": self.page_size, "query": query_filter},
            connection_path=["orders"],
        )

        records = self.transformer.orders(raw)
        watermark = _max_datetime(records, "created_at") or since

        return DataProviderResult(
            records=records,
            watermark=watermark,
            entity_type="orders",
            raw_count=len(raw),
        )

    async def _fetch_customers(
        self, since: Optional[datetime] = None, config: dict = {}
    ) -> DataProviderResult:
        """Fetch customer records from Shopify."""
        query_filter = f"updated_at:>'{since.isoformat()}'" if since else None
        raw = await self.client.paginated_query(
            CUSTOMERS_QUERY,
            variables={"first": self.page_size, "query": query_filter},
            connection_path=["customers"],
        )

        records = self.transformer.customers(raw)
        watermark = _max_datetime(records, "updated_at") or since

        return DataProviderResult(
            records=records,
            watermark=watermark,
            entity_type="customers",
            raw_count=len(raw),
        )

    async def _fetch_inventory(
        self, since: Optional[datetime] = None, config: dict = {}
    ) -> DataProviderResult:
        """Fetch inventory items and levels from Shopify."""
        query_filter = f"updated_at:>'{since.isoformat()}'" if since else None
        raw = await self.client.paginated_query(
            INVENTORY_QUERY,
            variables={"first": self.page_size, "query": query_filter},
            connection_path=["inventoryItems"],
        )

        records = self.transformer.inventory(raw)
        watermark = _max_datetime(records, "updated_at") or since

        return DataProviderResult(
            records=records,
            watermark=watermark,
            entity_type="inventory",
            raw_count=len(raw),
        )


def _max_datetime(records: list, attr: str) -> Optional[datetime]:
    """Get the maximum datetime value from a list of Pydantic models."""
    values = [getattr(r, attr) for r in records if getattr(r, attr, None)]
    return max(values) if values else None
