"""
WooCommerce Connector - Integration with WooCommerce stores

Uses WooCommerce REST API to access:
- Orders
- Products
- Customers
- Inventory
- Reports
"""
import httpx
from typing import Any, Dict, List, Optional
from datetime import date, timedelta
import logging
import hashlib
import hmac
import time
import urllib.parse

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

logger = logging.getLogger(__name__)


class WooCommerceConnector(BaseConnector):
    """
    Connector for WooCommerce stores (WordPress + WooCommerce plugin).

    Configuration:
    {
        "connector_type": "woocommerce",
        "api_base_url": "https://your-store.com",
        "api_key": "ck_your_consumer_key",
        "api_secret": "cs_your_consumer_secret"
    }
    """

    API_VERSION = "wc/v3"

    def __init__(self, config: ConnectorConfig, client_id: int):
        super().__init__(config, client_id)
        self._client: Optional[httpx.AsyncClient] = None

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

    @property
    def _base_url(self) -> str:
        store_url = self.config.api_base_url.rstrip("/")
        return f"{store_url}/wp-json/{self.API_VERSION}"

    async def connect(self) -> bool:
        """Initialize WooCommerce API client"""
        try:
            # WooCommerce uses HTTP Basic Auth with consumer key/secret
            auth = (self.config.api_key, self.config.api_secret)
            self._client = httpx.AsyncClient(
                base_url=self._base_url,
                auth=auth,
                timeout=self.config.timeout,
                headers={"Content-Type": "application/json"}
            )
            self.status = ConnectionStatus.CONNECTED
            logger.info(f"Connected to WooCommerce 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 WooCommerce connection"""
        try:
            if not self._client:
                await self.connect()

            response = await self._client.get("/")
            response.raise_for_status()
            data = response.json()

            return {
                "success": True,
                "message": "Connected to WooCommerce",
                "details": {
                    "store_name": data.get("store", {}).get("name"),
                    "version": data.get("version"),
                    "currency": data.get("settings", {}).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
    ) -> Any:
        """Make API request to WooCommerce"""
        if not self._client:
            await self.connect()

        endpoint = endpoint.lstrip("/")

        if method.upper() == "GET":
            response = await self._client.get(f"/{endpoint}", params=params)
        elif method.upper() == "POST":
            response = await self._client.post(f"/{endpoint}", json=data)
        elif method.upper() == "PUT":
            response = await self._client.put(f"/{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"""
        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 = {
                "after": f"{start}T00:00:00",
                "before": f"{end}T23:59:59",
                "per_page": 100,
                "status": "completed,processing"
            }

            orders = await self._request("GET", "/orders", params=params)

            total_revenue = sum(float(o.get("total", 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 WooCommerce 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 = {
                "after": f"{start}T00:00:00",
                "before": f"{end}T23:59:59",
                "per_page": 100,
                "status": "completed,processing"
            }

            orders = await self._request("GET", "/orders", params=params)

            # 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("name"),
                            "revenue": 0,
                            "quantity": 0
                        }
                    product_stats[pid]["revenue"] += float(item.get("total", 0))
                    product_stats[pid]["quantity"] += item.get("quantity", 1)

            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 by category"""
        start, end = self._parse_period(period)

        try:
            params = {
                "after": f"{start}T00:00:00",
                "before": f"{end}T23:59:59",
                "per_page": 100,
                "status": "completed,processing"
            }

            orders = await self._request("GET", "/orders", params=params)

            breakdown = {}
            for order in orders:
                for item in order.get("line_items", []):
                    # Get category from product
                    categories = item.get("meta_data", [])
                    cat_name = "Uncategorized"
                    for meta in categories:
                        if meta.get("key") == "_category":
                            cat_name = meta.get("value", "Uncategorized")
                            break

                    if cat_name not in breakdown:
                        breakdown[cat_name] = {"name": cat_name, "revenue": 0}
                    breakdown[cat_name]["revenue"] += float(item.get("total", 0))

            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 = {
                "after": f"{start}T00:00:00",
                "before": f"{end}T23:59:59",
                "per_page": min(limit, 100)
            }

            if status and status != "all":
                params["status"] = status
            if customer_id:
                params["customer"] = customer_id

            orders = await self._request("GET", "/orders", params=params)

            formatted_orders = []
            for o in orders:
                formatted_orders.append({
                    "id": str(o.get("id")),
                    "order_number": o.get("number"),
                    "status": o.get("status"),
                    "total": float(o.get("total", 0)),
                    "customer": f"{o.get('billing', {}).get('first_name', '')} {o.get('billing', {}).get('last_name', '')}",
                    "created_at": o.get("date_created")
                })

            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:
            o = await self._request("GET", f"/orders/{order_id}")

            return {
                "id": str(o.get("id")),
                "order_number": o.get("number"),
                "status": o.get("status"),
                "total": float(o.get("total", 0)),
                "subtotal": float(o.get("subtotal", 0)),
                "tax": float(o.get("total_tax", 0)),
                "shipping_total": float(o.get("shipping_total", 0)),
                "customer": {
                    "name": f"{o.get('billing', {}).get('first_name', '')} {o.get('billing', {}).get('last_name', '')}",
                    "email": o.get("billing", {}).get("email"),
                    "phone": o.get("billing", {}).get("phone")
                },
                "items": [
                    {
                        "product": item.get("name"),
                        "product_id": item.get("product_id"),
                        "quantity": item.get("quantity"),
                        "price": float(item.get("price", 0))
                    }
                    for item in o.get("line_items", [])
                ],
                "shipping": {
                    "address": f"{o.get('shipping', {}).get('address_1', '')} {o.get('shipping', {}).get('city', '')}",
                    "method": o.get("shipping_lines", [{}])[0].get("method_title") if o.get("shipping_lines") else None
                },
                "payment_method": o.get("payment_method_title"),
                "created_at": o.get("date_created")
            }
        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"),
                "payment_method": order.get("payment_method")
            }
        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"""
        try:
            order = await self.get_order_details(order_id)
            if not order:
                return {"error": "Order not found"}

            refund_data = {
                "amount": str(amount or order.get("total")),
                "reason": reason,
                "refunded_by": 1,  # Admin user ID
                "api_refund": True
            }

            if restock:
                refund_data["line_items"] = [
                    {"id": item.get("product_id"), "quantity": item.get("quantity")}
                    for item in order.get("items", [])
                ]

            data = await self._request("POST", f"/orders/{order_id}/refunds", data=refund_data)

            return {
                "refund_id": str(data.get("id")),
                "order_id": order_id,
                "amount": float(data.get("amount", 0)),
                "status": "processed"
            }
        except Exception as e:
            return {"error": str(e)}

    async def update_order_status(
        self,
        order_id: str,
        status: str,
        tracking_number: Optional[str] = None,
        note: Optional[str] = None
    ) -> Dict[str, Any]:
        """Update order status"""
        try:
            data = {"status": status}
            result = await self._request("PUT", f"/orders/{order_id}", data=data)

            # Add note if provided
            if note:
                await self._request("POST", f"/orders/{order_id}/notes", data={
                    "note": note,
                    "customer_note": False
                })

            return {
                "order_id": order_id,
                "status": result.get("status"),
                "updated_at": result.get("date_modified")
            }
        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 = {"per_page": min(limit, 100)}
            if status and status != "all":
                params["status"] = status
            if category:
                params["category"] = category
            if in_stock is not None:
                params["stock_status"] = "instock" if in_stock else "outofstock"

            products = await self._request("GET", "/products", params=params)

            formatted = [
                {
                    "id": str(p.get("id")),
                    "title": p.get("name"),
                    "price": float(p.get("price", 0) or 0),
                    "regular_price": float(p.get("regular_price", 0) or 0),
                    "stock": p.get("stock_quantity") or 0,
                    "stock_status": p.get("stock_status"),
                    "status": p.get("status"),
                    "sku": p.get("sku")
                }
                for p in products
            ]

            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:
            p = await self._request("GET", f"/products/{product_id}")

            return {
                "id": str(p.get("id")),
                "title": p.get("name"),
                "description": p.get("description"),
                "short_description": p.get("short_description"),
                "price": float(p.get("price", 0) or 0),
                "regular_price": float(p.get("regular_price", 0) or 0),
                "sale_price": float(p.get("sale_price", 0) or 0) if p.get("sale_price") else None,
                "stock": p.get("stock_quantity") or 0,
                "stock_status": p.get("stock_status"),
                "sku": p.get("sku"),
                "categories": [c.get("name") for c in p.get("categories", [])],
                "images": [img.get("src") for img in p.get("images", [])],
                "variations": p.get("variations", [])
            }
        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 = {"search": query, "per_page": min(limit, 100)}
            products = await self._request("GET", "/products", params=params)

            formatted = [
                {
                    "id": str(p.get("id")),
                    "title": p.get("name"),
                    "price": float(p.get("price", 0) or 0),
                    "stock": p.get("stock_quantity") or 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:
            params = {"per_page": 100}
            if include_out_of_stock:
                params["stock_status"] = "outofstock,onbackorder,instock"

            products = await self._request("GET", "/products", params=params)

            low_stock = []
            for p in products:
                qty = p.get("stock_quantity") or 0
                if qty <= threshold:
                    if include_out_of_stock or qty > 0:
                        low_stock.append({
                            "id": str(p.get("id")),
                            "title": p.get("name"),
                            "stock": qty,
                            "threshold": threshold,
                            "sku": p.get("sku")
                        })

            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:
            params = {"per_page": 100}
            products = await self._request("GET", "/products", params=params)

            inventory = [
                {
                    "product_id": str(p.get("id")),
                    "title": p.get("name"),
                    "sku": p.get("sku"),
                    "quantity": p.get("stock_quantity") or 0,
                    "stock_status": p.get("stock_status")
                }
                for p in products
            ]

            if product_ids:
                inventory = [i for i in inventory if i["product_id"] in product_ids]

            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}

    async def update_inventory(
        self,
        product_id: str,
        quantity: int,
        adjustment_type: str = "set",
        location: Optional[str] = None,
        reason: Optional[str] = None
    ) -> Dict[str, Any]:
        """Update inventory"""
        try:
            if adjustment_type == "adjust":
                # Get current stock and adjust
                product = await self.get_product_details(product_id)
                if product:
                    quantity = product.get("stock", 0) + quantity

            data = {"stock_quantity": quantity, "manage_stock": True}
            result = await self._request("PUT", f"/products/{product_id}", data=data)

            return {
                "product_id": product_id,
                "new_quantity": result.get("stock_quantity"),
                "updated_at": result.get("date_modified")
            }
        except Exception as e:
            return {"error": str(e)}

    # ==================== 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 = {"per_page": min(limit, 100)}

            customers = await self._request("GET", "/customers", params=params)

            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) or 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:
                c = await self._request("GET", f"/customers/{customer_id}")
            elif email:
                customers = await self._request("GET", "/customers", params={"email": email})
                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("billing", {}).get("phone"),
                "total_orders": c.get("orders_count", 0),
                "total_spent": float(c.get("total_spent", 0) or 0),
                "address": f"{c.get('billing', {}).get('address_1', '')} {c.get('billing', {}).get('city', '')}",
                "created_at": c.get("date_created")
            }
        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:
            params = {"per_page": 100, "orderby": "id", "order": "desc"}
            customers = await self._request("GET", "/customers", params=params)

            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) or 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:
            params = {"search": query, "per_page": min(limit, 100)}
            customers = await self._request("GET", "/customers", params=params)

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

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