"""
Custom API Connector - Connect to any REST API

This connector allows clients to integrate their existing API
(PHP, Node.js, Python, Java, etc.) with AI-360.

The client configures:
1. Base URL of their API
2. Authentication method
3. Endpoint mappings for each data type
4. Field mappings to translate their schema to standard format
"""
import httpx
from typing import Any, Dict, List, Optional
from datetime import date
import logging
import json

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

logger = logging.getLogger(__name__)


class CustomAPIConnector(BaseConnector):
    """
    Connector for custom REST APIs.

    Endpoint Configuration Example:
    {
        "endpoints": {
            "sales": "/api/sales",
            "sales_comparison": "/api/sales/compare",
            "orders": "/api/orders",
            "order_details": "/api/orders/{order_id}",
            "products": "/api/products",
            "product_details": "/api/products/{product_id}",
            "inventory": "/api/inventory",
            "customers": "/api/customers"
        },
        "field_mappings": {
            "order": {
                "id": "order_number",
                "total": "grand_total",
                "status": "order_status",
                "customer_name": "buyer_name"
            },
            "product": {
                "id": "sku",
                "title": "product_name",
                "price": "selling_price",
                "stock": "quantity_available"
            }
        }
    }
    """

    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.CUSTOM_API

    async def connect(self) -> bool:
        """Initialize HTTP client with authentication"""
        try:
            headers = self._build_auth_headers()
            self._client = httpx.AsyncClient(
                base_url=self.config.api_base_url,
                headers=headers,
                timeout=self.config.timeout,
            )
            self.status = ConnectionStatus.CONNECTED
            logger.info(f"Connected to API: {self.config.api_base_url}")
            return True
        except Exception as e:
            self._last_error = str(e)
            self.status = ConnectionStatus.ERROR
            logger.error(f"Failed to connect to API: {e}")
            return False

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

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

            # Try a simple request to verify connection
            test_endpoint = self.config.endpoints.get("test", "/api/health")
            response = await self._client.get(test_endpoint)

            if response.status_code < 400:
                return {
                    "success": True,
                    "message": "Connection successful",
                    "details": {"status_code": response.status_code}
                }
            else:
                return {
                    "success": False,
                    "message": f"API returned status {response.status_code}",
                    "details": {"response": response.text[:500]}
                }
        except Exception as e:
            return {
                "success": False,
                "message": str(e),
                "details": {}
            }

    def _build_auth_headers(self) -> Dict[str, str]:
        """Build authentication headers based on config"""
        headers = {"Content-Type": "application/json"}
        auth_type = self.config.auth_type or "none"

        if auth_type == "none":
            pass
        elif auth_type == "bearer":
            if self.config.api_key:
                headers[self.config.auth_header] = f"Bearer {self.config.api_key}"
        elif auth_type == "basic":
            import base64
            credentials = f"{self.config.api_key}:{self.config.api_secret}"
            encoded = base64.b64encode(credentials.encode()).decode()
            headers[self.config.auth_header] = f"Basic {encoded}"
        elif auth_type == "api_key":
            header_name = self.config.auth_header or "X-API-Key"
            headers[header_name] = self.config.api_key

        return headers

    async def _request(
        self,
        method: str,
        endpoint_key: str,
        params: Dict[str, Any] = None,
        data: Dict[str, Any] = None,
        path_params: Dict[str, str] = None
    ) -> Dict[str, Any]:
        """
        Make API request.

        Args:
            method: HTTP method
            endpoint_key: Key in endpoints config
            params: Query parameters
            data: Request body
            path_params: URL path parameters (e.g., {order_id})
        """
        if not self._client:
            await self.connect()

        endpoint = self.config.endpoints.get(endpoint_key)
        if not endpoint:
            raise ValueError(f"Endpoint not configured: {endpoint_key}")

        # Replace path parameters
        if path_params:
            for key, value in path_params.items():
                endpoint = endpoint.replace(f"{{{key}}}", str(value))

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

            response.raise_for_status()
            return response.json()

        except httpx.HTTPStatusError as e:
            logger.error(f"API error: {e.response.status_code} - {e.response.text}")
            raise
        except Exception as e:
            logger.error(f"Request failed: {e}")
            raise

    # ==================== 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:
            params = {"start_date": start_date, "end_date": end_date}
        else:
            start, end = self._parse_period(period)
            params = {"start_date": str(start), "end_date": str(end)}

        params["period"] = period

        try:
            data = await self._request("GET", "sales", params=params)
            return self._map_fields(data, "sales")
        except Exception as e:
            logger.error(f"Failed to get sales: {e}")
            return {
                "period": period,
                "total_revenue": 0,
                "order_count": 0,
                "average_order_value": 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"""
        params = {"current_period": current_period, "compare_to": compare_to}

        try:
            return await self._request("GET", "sales_comparison", params=params)
        except Exception:
            # Fallback: calculate from individual sales calls
            current_start, current_end = self._parse_period(current_period)

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

            current_sales = await self.get_sales(
                start_date=str(current_start),
                end_date=str(current_end)
            )
            prev_sales = await self.get_sales(
                start_date=str(prev_start),
                end_date=str(prev_end)
            )

            current_rev = current_sales.get("total_revenue", 0)
            prev_rev = prev_sales.get("total_revenue", 0)

            growth = ((current_rev - prev_rev) / prev_rev * 100) if prev_rev else 0

            return {
                "current_period": current_sales,
                "previous_period": prev_sales,
                "growth": {
                    "revenue_percent": round(growth, 2),
                    "orders_percent": 0
                }
            }

    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)
        params = {
            "start_date": str(start),
            "end_date": str(end),
            "sort_by": sort_by,
            "limit": limit
        }

        try:
            data = await self._request("GET", "top_products", params=params)
            if "products" in data:
                data["products"] = self._map_list(data["products"], "product")
            return data
        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"""
        start, end = self._parse_period(period)
        params = {
            "start_date": str(start),
            "end_date": str(end),
            "group_by": group_by
        }

        try:
            return await self._request("GET", "revenue_breakdown", params=params)
        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)
        params = {
            "start_date": str(start),
            "end_date": str(end),
            "limit": limit
        }
        if status and status != "all":
            params["status"] = status
        if customer_id:
            params["customer_id"] = customer_id

        try:
            data = await self._request("GET", "orders", params=params)
            if "orders" in data:
                data["orders"] = self._map_list(data["orders"], "order")
            return data
        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", "order_details",
                path_params={"order_id": order_id}
            )
            return self._map_fields(data, "order")
        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"""
        try:
            # Try dedicated status endpoint first
            return await self._request(
                "GET", "order_status",
                path_params={"order_id": order_id}
            )
        except Exception:
            # Fallback to order details
            order = await self.get_order_details(order_id)
            if order:
                return {
                    "order_id": order_id,
                    "status": order.get("status", "unknown"),
                    "tracking_number": order.get("tracking_number")
                }
            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"""
        data = {
            "order_id": order_id,
            "reason": reason,
            "restock": restock
        }
        if amount:
            data["amount"] = amount

        return await self._request("POST", "refunds", data=data)

    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"""
        data = {"status": status}
        if tracking_number:
            data["tracking_number"] = tracking_number
        if note:
            data["note"] = note

        return await self._request(
            "PUT", "order_details",
            path_params={"order_id": order_id},
            data=data
        )

    # ==================== 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"""
        params = {"limit": limit}
        if category:
            params["category"] = category
        if status and status != "all":
            params["status"] = status
        if in_stock is not None:
            params["in_stock"] = in_stock

        try:
            data = await self._request("GET", "products", params=params)
            if "products" in data:
                data["products"] = self._map_list(data["products"], "product")
            return data
        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", "product_details",
                path_params={"product_id": product_id}
            )
            return self._map_fields(data, "product")
        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"""
        params = {"query": query, "limit": limit}

        try:
            data = await self._request("GET", "product_search", params=params)
            if "products" in data:
                data["products"] = self._map_list(data["products"], "product")
            return data
        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"""
        params = {
            "threshold": threshold,
            "include_out_of_stock": include_out_of_stock
        }

        try:
            data = await self._request("GET", "low_stock", params=params)
            if "products" in data:
                data["products"] = self._map_list(data["products"], "product")
            return data
        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"""
        params = {}
        if product_ids:
            params["product_ids"] = ",".join(product_ids)
        if location:
            params["location"] = location

        try:
            return await self._request("GET", "inventory", params=params)
        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"""
        params = {"type": alert_type}

        try:
            return await self._request("GET", "inventory_alerts", params=params)
        except Exception as e:
            return {"alerts": [], "error": str(e)}

    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"""
        data = {
            "product_id": product_id,
            "quantity": quantity,
            "adjustment_type": adjustment_type
        }
        if location:
            data["location"] = location
        if reason:
            data["reason"] = reason

        return await self._request("POST", "inventory_update", data=data)

    # ==================== 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"""
        params = {"limit": limit}
        if status and status != "all":
            params["status"] = status
        if created_after:
            params["created_after"] = created_after
        if has_orders is not None:
            params["has_orders"] = has_orders

        try:
            data = await self._request("GET", "customers", params=params)
            if "customers" in data:
                data["customers"] = self._map_list(data["customers"], "customer")
            return data
        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"""
        if customer_id:
            try:
                data = await self._request(
                    "GET", "customer_details",
                    path_params={"customer_id": customer_id}
                )
                return self._map_fields(data, "customer")
            except Exception:
                pass

        # Search by email or phone
        params = {}
        if email:
            params["email"] = email
        if phone:
            params["phone"] = phone

        if params:
            try:
                data = await self._request("GET", "customer_search", params=params)
                customers = data.get("customers", [])
                if customers:
                    return self._map_fields(customers[0], "customer")
            except Exception:
                pass

        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"""
        params = {"period": period, "sort_by": sort_by, "limit": limit}

        try:
            data = await self._request("GET", "top_customers", params=params)
            if "customers" in data:
                data["customers"] = self._map_list(data["customers"], "customer")
            return data
        except Exception as e:
            return {"customers": [], "error": str(e)}

    async def search_customers(self, query: str, limit: int = 10) -> Dict[str, Any]:
        """Search customers"""
        params = {"query": query, "limit": limit}

        try:
            data = await self._request("GET", "customer_search", params=params)
            if "customers" in data:
                data["customers"] = self._map_list(data["customers"], "customer")
            return data
        except Exception as e:
            return {"customers": [], "query": query, "error": str(e)}


# Import timedelta for calculations
from datetime import timedelta
