"""Base email provider interface and data models."""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Dict, Any, Optional
import logging

logger = logging.getLogger(__name__)


@dataclass
class Attachment:
    """Email attachment metadata."""
    filename: str
    mime_type: str
    size: int  # in bytes
    attachment_id: Optional[str] = None

    def __repr__(self) -> str:
        return f"Attachment({self.filename}, {self.mime_type}, {self.size}B)"


@dataclass
class Email:
    """Unified email data model."""
    id: str
    subject: str
    sender: str
    sender_name: Optional[str] = None
    date: Optional[datetime] = None
    body: str = ""
    body_snippet: str = ""
    has_attachments: bool = False
    attachments: List[Attachment] = field(default_factory=list)
    headers: Dict[str, str] = field(default_factory=dict)
    labels: List[str] = field(default_factory=list)
    is_read: bool = False
    provider: str = "unknown"  # gmail, imap, microsoft, etc.

    def __post_init__(self):
        """Generate body_snippet if not provided."""
        if not self.body_snippet and self.body:
            self.body_snippet = self.body[:500]

    def __repr__(self) -> str:
        return f"Email(id={self.id}, from={self.sender}, subject={self.subject[:50]})"

    @property
    def attachment_types(self) -> List[str]:
        """Get list of attachment types."""
        return [a.mime_type.split('/')[-1] for a in self.attachments]

    @property
    def attachment_count(self) -> int:
        """Get count of attachments."""
        return len(self.attachments)


@dataclass
class ClassificationResult:
    """Result from email classification."""
    email_id: str
    category: str
    confidence: float
    method: str  # 'rule', 'ml', 'llm'
    probabilities: Dict[str, float] = field(default_factory=dict)
    needs_review: bool = False
    metadata: Dict[str, Any] = field(default_factory=dict)
    error: Optional[str] = None

    def __repr__(self) -> str:
        return f"ClassificationResult(id={self.email_id}, category={self.category}, conf={self.confidence:.2f})"


class BaseProvider(ABC):
    """Abstract base class for email providers."""

    def __init__(self, name: str = "base"):
        """Initialize provider."""
        self.name = name
        self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")

    @abstractmethod
    def connect(self, credentials: Dict[str, Any]) -> bool:
        """Establish connection to email provider.

        Args:
            credentials: Provider-specific credentials

        Returns:
            True if connected, False otherwise
        """
        pass

    @abstractmethod
    def disconnect(self) -> bool:
        """Close connection."""
        pass

    @abstractmethod
    def fetch_emails(
        self,
        limit: Optional[int] = None,
        filters: Optional[Dict[str, Any]] = None
    ) -> List[Email]:
        """Fetch emails from provider.

        Args:
            limit: Maximum number of emails to fetch
            filters: Provider-specific filters

        Returns:
            List of Email objects
        """
        pass

    @abstractmethod
    def update_labels(self, email_id: str, labels: List[str]) -> bool:
        """Update email labels/folders.

        Args:
            email_id: Email identifier
            labels: List of labels to set

        Returns:
            True if successful
        """
        pass

    @abstractmethod
    def batch_update(self, updates: List[Dict[str, Any]]) -> bool:
        """Batch update multiple emails.

        Args:
            updates: List of update dictionaries with email_id and labels

        Returns:
            True if successful
        """
        pass

    def is_connected(self) -> bool:
        """Check if provider is connected."""
        raise NotImplementedError


class MockProvider(BaseProvider):
    """
    Mock email provider for testing.

    IMPORTANT: This is a MOCK implementation for testing only.
    It does not connect to any real email service.
    All emails returned are synthetic/test data.
    """

    def __init__(self):
        """Initialize mock provider."""
        super().__init__(name="mock")
        self.connected = False
        self.mock_emails: List[Email] = []
        self.logger.warning("Using MOCK email provider - no real emails will be fetched")

    def connect(self, credentials: Dict[str, Any]) -> bool:
        """Mock connection."""
        self.logger.info("Mock provider: simulated connection")
        self.connected = True
        return True

    def disconnect(self) -> bool:
        """Mock disconnection."""
        self.logger.info("Mock provider: simulated disconnection")
        self.connected = False
        return True

    def fetch_emails(
        self,
        limit: Optional[int] = None,
        filters: Optional[Dict[str, Any]] = None
    ) -> List[Email]:
        """Return mock emails."""
        if not self.connected:
            self.logger.warning("Mock provider: not connected, returning empty list")
            return []

        result = self.mock_emails[:limit] if limit else self.mock_emails
        self.logger.info(f"Mock provider: returning {len(result)} mock emails")
        return result

    def update_labels(self, email_id: str, labels: List[str]) -> bool:
        """Mock label update."""
        self.logger.debug(f"Mock provider: label update for {email_id}")
        return True

    def batch_update(self, updates: List[Dict[str, Any]]) -> bool:
        """Mock batch update."""
        self.logger.info(f"Mock provider: batch update for {len(updates)} emails")
        return True

    def is_connected(self) -> bool:
        """Check mock connection."""
        return self.connected

    def add_mock_email(self, email: Email) -> None:
        """Add a mock email for testing."""
        self.mock_emails.append(email)
