"""
Shopify Connector - Integration with Shopify stores

Uses Shopify Admin API to access:
- Orders
- Products
- Customers
- Inventory
- Analytics
"""
import httpx
from typing import Any, Dict, List, Optional
from datetime import date, timedelta
import logging

from app.connectors.base import (
    BaseConnector,
    ConnectorConfig,
    ConnectorType,
    ConnectionStatus,
)

logger = logging.getLogger(__name__)


class ShopifyConnector(BaseConnector):
    """
    Connector for Shopify stores.

    Configuration:
    {
        "connector_type": "shopify",
        "api_base_url": "https://your-store.myshopify.com",
        "api_key": "your-admin-api-access-token",
        "api_version": "2024-01"  # Optional, defaults to latest
    }
    """

    API_VERSION = "2024-01"

    def __init__(self, config: ConnectorConfig, client_id: int):
        super().__init__(config, client_id)
        self._client: Optional[httpx.AsyncClient] = None
        self._api_version = config.endpoints.get("api_version", self.API_VERSION)

    @property
    def connector_type(self) -> ConnectorType:
        return ConnectorType.SHOPIFY

    @property
    def _base_url(self) -> str:
        store_url = self.config.api_base_url.rstrip("/")
        return f"{store_url}/admin/api/{self._api_version}"

    async def connect(self) -> bool:
        """Initialize Shopify API client"""
        try:
            headers = {
                "X-Shopify-Access-Token": self.config.api_key,
                "Content-Type": "application/json"
            }
            self._client = httpx.AsyncClient(
                base_url=self._base_url,
                headers=headers,
                timeout=self.config.timeout
            )
            self.status = ConnectionStatus.CONNECTED
            logger.info(f"Connected to Shopify store: {self.config.api_base_url}")
            return True
        except Exception as e:
            self._last_error = str(e)
            self.status = ConnectionStatus.ERROR
            return False

    async def disconnect(self) -> None:
        if self._client:
            await self._client.aclose()
            self._client = None
        self.status = ConnectionStatus.DISCONNECTED

    async def test_connection(self) -> Dict[str, Any]:
        """Test Shopify connection"""
        try:
            if not self._client:
                await self.connect()

            response = await self._client.get("/shop.json")
            response.raise_for_status()
            shop = response.json().get("shop", {})

            return {
                "success": True,
                "message": "Connected to Shopify",
                "details": {
                    "shop_name": shop.get("name"),
                    "domain": shop.get("domain"),
                    "currency": shop.get("currency")
                }
            }
        except Exception as e:
            return {"success": False, "message": str(e), "details": {}}

    async def _request(
        self,
        method: str,
        endpoint: str,
        params: Dict = None,
        data: Dict = None
    ) -> Dict[str, Any]:
        """Make API request to Shopify"""
        if not self._client:
            await self.connect()

        if method.upper() == "GET":
            response = await self._client.get(endpoint, params=params)
        elif method.upper() == "POST":
            response = await self._client.post(endpoint, json=data)
        elif method.upper() == "PUT":
            response = await self._client.put(endpoint, json=data)
        else:
            raise ValueError(f"Unsupported method: {method}")

        response.raise_for_status()
        return response.json()

    # ==================== SALES METHODS ====================

    async def get_sales(
        self,
        period: str = "today",
        start_date: Optional[str] = None,
        end_date: Optional[str] = None
    ) -> Dict[str, Any]:
        """Get sales data from orders"""
        if start_date and end_date:
            start, end = start_date, end_date
        else:
            s, e = self._parse_period(period)
            start, end = str(s), str(e)

        try:
            # Get orders in date range
            params = {
                "status": "any",
                "created_at_min": f"{start}T00:00:00Z",
                "created_at_max": f"{end}T23:59:59Z",
                "limit": 250
            }
            data = await self._request("GET", "/orders.json", params=params)
            orders = data.get("orders", [])

            # Calculate totals
            total_revenue = sum(float(o.get("total_price", 0)) for o in orders)
            order_count = len(orders)
            avg_order = total_revenue / order_count if order_count else 0

            return {
                "period": period,
                "total_revenue": round(total_revenue, 2),
                "order_count": order_count,
                "average_order_value": round(avg_order, 2),
                "currency": orders[0].get("currency", "INR") if orders else "INR"
            }
        except Exception as e:
            logger.error(f"Failed to get Shopify sales: {e}")
            return {"period": period, "total_revenue": 0, "order_count": 0, "error": str(e)}

    async def get_sales_comparison(
        self,
        current_period: str = "this_month",
        compare_to: str = "previous"
    ) -> Dict[str, Any]:
        """Compare sales between periods"""
        current_start, current_end = self._parse_period(current_period)

        if compare_to == "previous":
            delta = current_end - current_start
            prev_end = current_start - timedelta(days=1)
            prev_start = prev_end - delta
        else:
            prev_start = current_start.replace(year=current_start.year - 1)
            prev_end = current_end.replace(year=current_end.year - 1)

        current = await self.get_sales(start_date=str(current_start), end_date=str(current_end))
        previous = await self.get_sales(start_date=str(prev_start), end_date=str(prev_end))

        curr_rev = current.get("total_revenue", 0)
        prev_rev = previous.get("total_revenue", 0)
        growth = ((curr_rev - prev_rev) / prev_rev * 100) if prev_rev else 0

        return {
            "current_period": current,
            "previous_period": previous,
            "growth": {"revenue_percent": round(growth, 2)}
        }

    async def get_top_products(
        self,
        period: str = "this_month",
        sort_by: str = "revenue",
        limit: int = 10
    ) -> Dict[str, Any]:
        """Get top selling products"""
        start, end = self._parse_period(period)

        try:
            params = {
                "status": "any",
                "created_at_min": f"{start}T00:00:00Z",
                "created_at_max": f"{end}T23:59:59Z",
                "limit": 250
            }
            data = await self._request("GET", "/orders.json", params=params)
            orders = data.get("orders", [])

            # Aggregate by product
            product_stats = {}
            for order in orders:
                for item in order.get("line_items", []):
                    pid = str(item.get("product_id"))
                    if pid not in product_stats:
                        product_stats[pid] = {
                            "id": pid,
                            "title": item.get("title"),
                            "revenue": 0,
                            "quantity": 0
                        }
                    product_stats[pid]["revenue"] += float(item.get("price", 0)) * item.get("quantity", 1)
                    product_stats[pid]["quantity"] += item.get("quantity", 1)

            # Sort and limit
            products = list(product_stats.values())
            products.sort(key=lambda x: x[sort_by], reverse=True)

            return {"products": products[:limit]}
        except Exception as e:
            return {"products": [], "error": str(e)}

    async def get_revenue_breakdown(
        self,
        period: str = "this_month",
        group_by: str = "category"
    ) -> Dict[str, Any]:
        """Get revenue breakdown - Shopify doesn't have built-in categories"""
        # For Shopify, we'll group by product type or vendor
        start, end = self._parse_period(period)

        try:
            params = {
                "status": "any",
                "created_at_min": f"{start}T00:00:00Z",
                "created_at_max": f"{end}T23:59:59Z",
                "limit": 250
            }
            data = await self._request("GET", "/orders.json", params=params)
            orders = data.get("orders", [])

            breakdown = {}
            for order in orders:
                for item in order.get("line_items", []):
                    key = item.get("vendor", "Unknown")
                    if key not in breakdown:
                        breakdown[key] = {"name": key, "revenue": 0}
                    breakdown[key]["revenue"] += float(item.get("price", 0)) * item.get("quantity", 1)

            return {"breakdown": list(breakdown.values())}
        except Exception as e:
            return {"breakdown": [], "error": str(e)}

    # ==================== ORDER METHODS ====================

    async def get_orders(
        self,
        status: Optional[str] = None,
        period: str = "this_week",
        customer_id: Optional[str] = None,
        limit: int = 20
    ) -> Dict[str, Any]:
        """Get list of orders"""
        start, end = self._parse_period(period)

        try:
            params = {
                "created_at_min": f"{start}T00:00:00Z",
                "created_at_max": f"{end}T23:59:59Z",
                "limit": min(limit, 250),
                "status": "any"
            }

            if status and status != "all":
                status_map = {
                    "pending": "pending",
                    "processing": "open",
                    "shipped": "open",
                    "delivered": "closed",
                    "cancelled": "cancelled"
                }
                params["status"] = status_map.get(status, status)

            data = await self._request("GET", "/orders.json", params=params)
            orders = data.get("orders", [])

            formatted_orders = []
            for o in orders:
                formatted_orders.append({
                    "id": str(o.get("id")),
                    "order_number": o.get("name"),
                    "status": o.get("fulfillment_status") or "unfulfilled",
                    "total": float(o.get("total_price", 0)),
                    "customer": o.get("customer", {}).get("first_name", "") + " " + o.get("customer", {}).get("last_name", ""),
                    "created_at": o.get("created_at")
                })

            return {"orders": formatted_orders, "total_count": len(formatted_orders)}
        except Exception as e:
            return {"orders": [], "total_count": 0, "error": str(e)}

    async def get_order_details(self, order_id: str) -> Optional[Dict[str, Any]]:
        """Get order details"""
        try:
            data = await self._request("GET", f"/orders/{order_id}.json")
            o = data.get("order", {})

            return {
                "id": str(o.get("id")),
                "order_number": o.get("name"),
                "status": o.get("fulfillment_status") or "unfulfilled",
                "financial_status": o.get("financial_status"),
                "total": float(o.get("total_price", 0)),
                "subtotal": float(o.get("subtotal_price", 0)),
                "tax": float(o.get("total_tax", 0)),
                "customer": {
                    "name": f"{o.get('customer', {}).get('first_name', '')} {o.get('customer', {}).get('last_name', '')}",
                    "email": o.get("customer", {}).get("email"),
                    "phone": o.get("customer", {}).get("phone")
                },
                "items": [
                    {
                        "product": item.get("title"),
                        "quantity": item.get("quantity"),
                        "price": float(item.get("price", 0))
                    }
                    for item in o.get("line_items", [])
                ],
                "shipping": {
                    "address": o.get("shipping_address", {}).get("address1"),
                    "tracking": o.get("fulfillments", [{}])[0].get("tracking_number") if o.get("fulfillments") else None
                },
                "created_at": o.get("created_at")
            }
        except Exception as e:
            logger.error(f"Failed to get order {order_id}: {e}")
            return None

    async def get_order_status(self, order_id: str) -> Optional[Dict[str, Any]]:
        """Get order status"""
        order = await self.get_order_details(order_id)
        if order:
            return {
                "order_id": order_id,
                "status": order.get("status"),
                "financial_status": order.get("financial_status"),
                "tracking_number": order.get("shipping", {}).get("tracking")
            }
        return None

    async def create_refund(
        self,
        order_id: str,
        amount: Optional[float] = None,
        reason: str = "",
        restock: bool = True
    ) -> Dict[str, Any]:
        """Create a refund in Shopify"""
        try:
            # Get order details first
            order = await self.get_order_details(order_id)
            if not order:
                return {"error": "Order not found"}

            refund_data = {
                "refund": {
                    "note": reason,
                    "notify": True,
                    "shipping": {"full_refund": True} if not amount else {},
                    "refund_line_items": [],
                    "transactions": [{
                        "kind": "refund",
                        "amount": amount or order.get("total")
                    }]
                }
            }

            data = await self._request("POST", f"/orders/{order_id}/refunds.json", data=refund_data)
            return {
                "refund_id": str(data.get("refund", {}).get("id")),
                "order_id": order_id,
                "amount": amount or order.get("total"),
                "status": "processed"
            }
        except Exception as e:
            return {"error": str(e)}

    # ==================== PRODUCT METHODS ====================

    async def get_products(
        self,
        category: Optional[str] = None,
        status: Optional[str] = None,
        in_stock: Optional[bool] = None,
        limit: int = 20
    ) -> Dict[str, Any]:
        """Get list of products"""
        try:
            params = {"limit": min(limit, 250)}
            if status and status != "all":
                params["status"] = status

            data = await self._request("GET", "/products.json", params=params)
            products = data.get("products", [])

            formatted = []
            for p in products:
                variant = p.get("variants", [{}])[0]
                formatted.append({
                    "id": str(p.get("id")),
                    "title": p.get("title"),
                    "price": float(variant.get("price", 0)),
                    "stock": variant.get("inventory_quantity", 0),
                    "status": p.get("status"),
                    "vendor": p.get("vendor")
                })

            return {"products": formatted}
        except Exception as e:
            return {"products": [], "error": str(e)}

    async def get_product_details(self, product_id: str) -> Optional[Dict[str, Any]]:
        """Get product details"""
        try:
            data = await self._request("GET", f"/products/{product_id}.json")
            p = data.get("product", {})

            variants = [
                {
                    "id": str(v.get("id")),
                    "title": v.get("title"),
                    "price": float(v.get("price", 0)),
                    "stock": v.get("inventory_quantity", 0),
                    "sku": v.get("sku")
                }
                for v in p.get("variants", [])
            ]

            return {
                "id": str(p.get("id")),
                "title": p.get("title"),
                "description": p.get("body_html"),
                "price": float(p.get("variants", [{}])[0].get("price", 0)),
                "stock": sum(v["stock"] for v in variants),
                "variants": variants,
                "vendor": p.get("vendor"),
                "product_type": p.get("product_type")
            }
        except Exception as e:
            logger.error(f"Failed to get product {product_id}: {e}")
            return None

    async def search_products(self, query: str, limit: int = 10) -> Dict[str, Any]:
        """Search products"""
        try:
            params = {"title": query, "limit": min(limit, 250)}
            data = await self._request("GET", "/products.json", params=params)
            products = data.get("products", [])

            formatted = [
                {
                    "id": str(p.get("id")),
                    "title": p.get("title"),
                    "price": float(p.get("variants", [{}])[0].get("price", 0)),
                    "stock": p.get("variants", [{}])[0].get("inventory_quantity", 0)
                }
                for p in products
            ]

            return {"products": formatted, "query": query}
        except Exception as e:
            return {"products": [], "query": query, "error": str(e)}

    async def get_low_stock_products(
        self,
        threshold: int = 10,
        include_out_of_stock: bool = True
    ) -> Dict[str, Any]:
        """Get low stock products"""
        try:
            data = await self._request("GET", "/products.json", params={"limit": 250})
            products = data.get("products", [])

            low_stock = []
            for p in products:
                for v in p.get("variants", []):
                    qty = v.get("inventory_quantity", 0)
                    if qty <= threshold:
                        if include_out_of_stock or qty > 0:
                            low_stock.append({
                                "id": str(p.get("id")),
                                "variant_id": str(v.get("id")),
                                "title": f"{p.get('title')} - {v.get('title')}",
                                "stock": qty,
                                "threshold": threshold
                            })

            return {"products": low_stock}
        except Exception as e:
            return {"products": [], "error": str(e)}

    # ==================== INVENTORY METHODS ====================

    async def get_inventory(
        self,
        product_ids: Optional[List[str]] = None,
        location: Optional[str] = None
    ) -> Dict[str, Any]:
        """Get inventory levels"""
        try:
            # Get inventory levels
            data = await self._request("GET", "/inventory_levels.json", params={"limit": 250})
            levels = data.get("inventory_levels", [])

            inventory = [
                {
                    "inventory_item_id": str(l.get("inventory_item_id")),
                    "location_id": str(l.get("location_id")),
                    "quantity": l.get("available", 0)
                }
                for l in levels
            ]

            return {"inventory": inventory}
        except Exception as e:
            return {"inventory": [], "error": str(e)}

    async def get_inventory_alerts(self, alert_type: str = "all") -> Dict[str, Any]:
        """Get inventory alerts"""
        low_stock = await self.get_low_stock_products(threshold=10)
        alerts = []

        for product in low_stock.get("products", []):
            if product["stock"] == 0:
                alerts.append({
                    "type": "out_of_stock",
                    "product_id": product["id"],
                    "title": product["title"],
                    "current": 0
                })
            else:
                alerts.append({
                    "type": "low_stock",
                    "product_id": product["id"],
                    "title": product["title"],
                    "current": product["stock"],
                    "threshold": product["threshold"]
                })

        if alert_type != "all":
            alerts = [a for a in alerts if a["type"] == alert_type]

        return {"alerts": alerts}

    # ==================== CUSTOMER METHODS ====================

    async def get_customers(
        self,
        status: Optional[str] = None,
        created_after: Optional[str] = None,
        has_orders: Optional[bool] = None,
        limit: int = 20
    ) -> Dict[str, Any]:
        """Get list of customers"""
        try:
            params = {"limit": min(limit, 250)}
            if created_after:
                params["created_at_min"] = created_after

            data = await self._request("GET", "/customers.json", params=params)
            customers = data.get("customers", [])

            formatted = [
                {
                    "id": str(c.get("id")),
                    "name": f"{c.get('first_name', '')} {c.get('last_name', '')}",
                    "email": c.get("email"),
                    "orders": c.get("orders_count", 0),
                    "total_spent": float(c.get("total_spent", 0))
                }
                for c in customers
            ]

            return {"customers": formatted}
        except Exception as e:
            return {"customers": [], "error": str(e)}

    async def get_customer_details(
        self,
        customer_id: Optional[str] = None,
        email: Optional[str] = None,
        phone: Optional[str] = None
    ) -> Optional[Dict[str, Any]]:
        """Get customer details"""
        try:
            if customer_id:
                data = await self._request("GET", f"/customers/{customer_id}.json")
                c = data.get("customer", {})
            elif email:
                data = await self._request("GET", "/customers/search.json", params={"query": f"email:{email}"})
                customers = data.get("customers", [])
                c = customers[0] if customers else {}
            else:
                return None

            if not c:
                return None

            return {
                "id": str(c.get("id")),
                "name": f"{c.get('first_name', '')} {c.get('last_name', '')}",
                "email": c.get("email"),
                "phone": c.get("phone"),
                "total_orders": c.get("orders_count", 0),
                "total_spent": float(c.get("total_spent", 0)),
                "created_at": c.get("created_at")
            }
        except Exception as e:
            logger.error(f"Failed to get customer: {e}")
            return None

    async def get_top_customers(
        self,
        period: str = "all_time",
        sort_by: str = "revenue",
        limit: int = 10
    ) -> Dict[str, Any]:
        """Get top customers"""
        try:
            # Shopify doesn't have direct top customers endpoint
            # Get all customers and sort
            data = await self._request("GET", "/customers.json", params={"limit": 250})
            customers = data.get("customers", [])

            formatted = [
                {
                    "id": str(c.get("id")),
                    "name": f"{c.get('first_name', '')} {c.get('last_name', '')}",
                    "email": c.get("email"),
                    "revenue": float(c.get("total_spent", 0)),
                    "orders": c.get("orders_count", 0)
                }
                for c in customers
            ]

            # Sort by revenue or orders
            sort_key = "revenue" if sort_by == "revenue" else "orders"
            formatted.sort(key=lambda x: x[sort_key], reverse=True)

            return {"customers": formatted[:limit]}
        except Exception as e:
            return {"customers": [], "error": str(e)}

    async def search_customers(self, query: str, limit: int = 10) -> Dict[str, Any]:
        """Search customers"""
        try:
            data = await self._request("GET", "/customers/search.json", params={"query": query})
            customers = data.get("customers", [])

            formatted = [
                {
                    "id": str(c.get("id")),
                    "name": f"{c.get('first_name', '')} {c.get('last_name', '')}",
                    "email": c.get("email")
                }
                for c in customers[:limit]
            ]

            return {"customers": formatted, "query": query}
        except Exception as e:
            return {"customers": [], "query": query, "error": str(e)}
