"""
Base Connector Interface - Defines the contract for all connectors
"""
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
from enum import Enum
from datetime import datetime, date, timedelta
import logging

logger = logging.getLogger(__name__)


class ConnectorType(str, Enum):
    """Types of supported connectors"""
    CUSTOM_API = "custom_api"           # Any REST API
    CUSTOM_DATABASE = "custom_database"  # Direct database connection
    SHOPIFY = "shopify"
    WOOCOMMERCE = "woocommerce"
    MAGENTO = "magento"
    OPENCART = "opencart"
    PRESTASHOP = "prestashop"
    CUSTOM_POS = "custom_pos"
    WEBHOOK = "webhook"                  # Client pushes data via webhook


class ConnectionStatus(str, Enum):
    """Connection status"""
    CONNECTED = "connected"
    DISCONNECTED = "disconnected"
    ERROR = "error"
    PENDING = "pending"


class ConnectorConfig(BaseModel):
    """Configuration for a connector"""
    connector_type: ConnectorType
    name: str = Field(..., description="Human-readable name for this connection")

    # API Configuration
    api_base_url: Optional[str] = None
    api_key: Optional[str] = None
    api_secret: Optional[str] = None
    auth_type: Optional[str] = Field(default="bearer", description="none, bearer, basic, api_key, oauth2")
    auth_header: Optional[str] = Field(default="Authorization", description="Header name for auth")

    # Database Configuration
    db_type: Optional[str] = None  # mysql, postgresql, sqlserver, mongodb
    db_host: Optional[str] = None
    db_port: Optional[int] = None
    db_name: Optional[str] = None
    db_user: Optional[str] = None
    db_password: Optional[str] = None
    db_ssl: Optional[bool] = False

    # Endpoint Mappings (for custom API)
    endpoints: Optional[Dict[str, str]] = Field(default_factory=dict)

    # Query Mappings (for custom database)
    queries: Optional[Dict[str, str]] = Field(default_factory=dict)

    # Field Mappings (map client fields to standard fields)
    field_mappings: Optional[Dict[str, Dict[str, str]]] = Field(default_factory=dict)

    # Additional settings
    timeout: int = 30
    retry_count: int = 3
    rate_limit: Optional[int] = None  # requests per minute

    # Metadata
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None


class BaseConnector(ABC):
    """
    Base class for all connectors.

    A connector is responsible for:
    1. Connecting to a client's data source
    2. Translating tool requests to source-specific queries
    3. Mapping response data to standard format
    4. Handling authentication and errors
    """

    def __init__(self, config: ConnectorConfig, client_id: int):
        self.config = config
        self.client_id = client_id
        self.status = ConnectionStatus.PENDING
        self._last_error: Optional[str] = None

    @property
    @abstractmethod
    def connector_type(self) -> ConnectorType:
        """Return the connector type"""
        pass

    @abstractmethod
    async def connect(self) -> bool:
        """
        Establish connection to the data source.
        Returns True if successful.
        """
        pass

    @abstractmethod
    async def disconnect(self) -> None:
        """Close the connection"""
        pass

    @abstractmethod
    async def test_connection(self) -> Dict[str, Any]:
        """
        Test the connection and return status info.
        Returns dict with 'success', 'message', and optional 'details'
        """
        pass

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

    @abstractmethod
    async def get_sales(
        self,
        period: str = "today",
        start_date: Optional[str] = None,
        end_date: Optional[str] = None
    ) -> Dict[str, Any]:
        """Get sales data for a period"""
        pass

    @abstractmethod
    async def get_sales_comparison(
        self,
        current_period: str = "this_month",
        compare_to: str = "previous"
    ) -> Dict[str, Any]:
        """Compare sales between periods"""
        pass

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

    @abstractmethod
    async def get_revenue_breakdown(
        self,
        period: str = "this_month",
        group_by: str = "category"
    ) -> Dict[str, Any]:
        """Get revenue breakdown"""
        pass

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

    @abstractmethod
    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"""
        pass

    @abstractmethod
    async def get_order_details(self, order_id: str) -> Optional[Dict[str, Any]]:
        """Get details of a specific order"""
        pass

    @abstractmethod
    async def get_order_status(self, order_id: str) -> Optional[Dict[str, Any]]:
        """Get order status and tracking info"""
        pass

    async def create_refund(
        self,
        order_id: str,
        amount: Optional[float] = None,
        reason: str = "",
        restock: bool = True
    ) -> Dict[str, Any]:
        """Create a refund - optional, not all systems support this"""
        raise NotImplementedError("Refunds not supported by this connector")

    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 - optional"""
        raise NotImplementedError("Order updates not supported by this connector")

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

    @abstractmethod
    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"""
        pass

    @abstractmethod
    async def get_product_details(self, product_id: str) -> Optional[Dict[str, Any]]:
        """Get product details"""
        pass

    @abstractmethod
    async def search_products(self, query: str, limit: int = 10) -> Dict[str, Any]:
        """Search products"""
        pass

    @abstractmethod
    async def get_low_stock_products(
        self,
        threshold: int = 10,
        include_out_of_stock: bool = True
    ) -> Dict[str, Any]:
        """Get low stock products"""
        pass

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

    @abstractmethod
    async def get_inventory(
        self,
        product_ids: Optional[List[str]] = None,
        location: Optional[str] = None
    ) -> Dict[str, Any]:
        """Get inventory levels"""
        pass

    @abstractmethod
    async def get_inventory_alerts(self, alert_type: str = "all") -> Dict[str, Any]:
        """Get inventory alerts"""
        pass

    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 - optional"""
        raise NotImplementedError("Inventory updates not supported by this connector")

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

    @abstractmethod
    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"""
        pass

    @abstractmethod
    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"""
        pass

    @abstractmethod
    async def get_top_customers(
        self,
        period: str = "all_time",
        sort_by: str = "revenue",
        limit: int = 10
    ) -> Dict[str, Any]:
        """Get top customers"""
        pass

    @abstractmethod
    async def search_customers(self, query: str, limit: int = 10) -> Dict[str, Any]:
        """Search customers"""
        pass

    # ==================== HELPER METHODS ====================

    def _parse_period(self, period: str) -> tuple[date, date]:
        """
        Convert period string to start and end dates.

        Args:
            period: today, yesterday, this_week, last_week, this_month, etc.

        Returns:
            Tuple of (start_date, end_date)
        """
        today = date.today()

        if period == "today":
            return today, today
        elif period == "yesterday":
            yesterday = today - timedelta(days=1)
            return yesterday, yesterday
        elif period == "this_week":
            start = today - timedelta(days=today.weekday())
            return start, today
        elif period == "last_week":
            end = today - timedelta(days=today.weekday() + 1)
            start = end - timedelta(days=6)
            return start, end
        elif period == "this_month":
            start = today.replace(day=1)
            return start, today
        elif period == "last_month":
            first_this_month = today.replace(day=1)
            end = first_this_month - timedelta(days=1)
            start = end.replace(day=1)
            return start, end
        elif period == "this_quarter":
            quarter = (today.month - 1) // 3
            start = today.replace(month=quarter * 3 + 1, day=1)
            return start, today
        elif period == "this_year":
            start = today.replace(month=1, day=1)
            return start, today
        elif period == "last_year":
            start = today.replace(year=today.year - 1, month=1, day=1)
            end = today.replace(year=today.year - 1, month=12, day=31)
            return start, end
        else:
            # Default to this month
            start = today.replace(day=1)
            return start, today

    def _map_fields(self, data: Dict[str, Any], mapping_key: str) -> Dict[str, Any]:
        """
        Map source fields to standard fields using configured mappings.

        Args:
            data: Source data dict
            mapping_key: Key in field_mappings config (e.g., 'order', 'product')

        Returns:
            Data with mapped field names
        """
        mappings = self.config.field_mappings.get(mapping_key, {})
        if not mappings:
            return data

        result = {}
        for std_field, source_field in mappings.items():
            if source_field in data:
                result[std_field] = data[source_field]
            elif std_field in data:
                result[std_field] = data[std_field]

        # Include unmapped fields
        for key, value in data.items():
            if key not in result:
                result[key] = value

        return result

    def _map_list(self, data_list: List[Dict], mapping_key: str) -> List[Dict]:
        """Map fields for a list of items"""
        return [self._map_fields(item, mapping_key) for item in data_list]

    @property
    def is_connected(self) -> bool:
        return self.status == ConnectionStatus.CONNECTED

    @property
    def last_error(self) -> Optional[str]:
        return self._last_error

    def __repr__(self) -> str:
        return f"<{self.__class__.__name__}: {self.config.name} ({self.status.value})>"
