"""
Connection management infrastructure for MarkiTect.

Provides HTTP session pooling, database connection management,
and resource lifecycle management with proper cleanup.
"""

import asyncio
import sqlite3
from typing import Optional, Dict, Any
from contextlib import asynccontextmanager
from dataclasses import dataclass
import aiohttp
from infrastructure.logging import get_logger

logger = get_logger(__name__)


@dataclass
class DataSourceConfig:
    """Configuration for data source connections."""

    # HTTP Configuration
    gitea_base_url: str
    gitea_token: str
    connection_pool_size: int = 20
    connection_per_host: int = 5
    request_timeout: int = 30
    keepalive_timeout: int = 60

    # Database Configuration
    database_path: str = "markitect.db"
    database_pool_size: int = 10
    database_timeout: int = 30

    # Retry Configuration
    max_retries: int = 3
    retry_backoff_factor: float = 1.5
    retry_base_delay: float = 1.0


class ConnectionManager:
    """
    Manages connection pooling for HTTP and database operations.

    Provides centralized resource management with proper lifecycle
    handling, connection pooling, and automatic cleanup.
    """

    def __init__(self, config: DataSourceConfig):
        self.config = config
        self._http_session: Optional[aiohttp.ClientSession] = None
        self._db_pool: Optional[sqlite3.Connection] = None
        self._lock = asyncio.Lock()

    async def get_http_session(self) -> aiohttp.ClientSession:
        """
        Get HTTP session with connection pooling.

        Returns:
            Configured aiohttp.ClientSession with connection pooling,
            timeout settings, and authentication headers.
        """
        if self._http_session is None or self._http_session.closed:
            async with self._lock:
                if self._http_session is None or self._http_session.closed:
                    await self._create_http_session()

        return self._http_session

    async def _create_http_session(self):
        """Create new HTTP session with optimized settings."""
        connector = aiohttp.TCPConnector(
            limit=self.config.connection_pool_size,
            limit_per_host=self.config.connection_per_host,
            keepalive_timeout=self.config.keepalive_timeout,
            enable_cleanup_closed=True
        )

        timeout = aiohttp.ClientTimeout(total=self.config.request_timeout)

        headers = {}
        if self.config.gitea_token:
            headers['Authorization'] = f'token {self.config.gitea_token}'

        self._http_session = aiohttp.ClientSession(
            base_url=self.config.gitea_base_url,
            connector=connector,
            timeout=timeout,
            headers=headers
        )

        logger.info(f"Created HTTP session with pool size {self.config.connection_pool_size}")

    def get_database_connection(self) -> sqlite3.Connection:
        """
        Get database connection with optimized settings.

        Returns:
            Configured SQLite connection with proper timeout
            and performance settings.
        """
        if self._db_pool is None:
            self._create_database_connection()

        return self._db_pool

    def _create_database_connection(self):
        """Create database connection with optimized settings."""
        self._db_pool = sqlite3.connect(
            self.config.database_path,
            timeout=self.config.database_timeout,
            check_same_thread=False
        )

        # Optimize SQLite settings for performance
        self._db_pool.execute("PRAGMA journal_mode=WAL")
        self._db_pool.execute("PRAGMA synchronous=NORMAL")
        self._db_pool.execute("PRAGMA cache_size=10000")
        self._db_pool.execute("PRAGMA temp_store=MEMORY")

        logger.info(f"Created database connection to {self.config.database_path}")

    @asynccontextmanager
    async def transaction(self):
        """
        Context manager for database transactions.

        Automatically handles commit/rollback and ensures
        proper resource cleanup.
        """
        conn = self.get_database_connection()
        conn.execute("BEGIN")

        try:
            yield conn
            conn.commit()
            logger.debug("Transaction committed successfully")
        except Exception as e:
            conn.rollback()
            logger.error(f"Transaction rolled back due to error: {e}")
            raise

    async def close(self):
        """Clean up all connections and resources."""
        if self._http_session and not self._http_session.closed:
            await self._http_session.close()
            logger.info("HTTP session closed")

        if self._db_pool:
            self._db_pool.close()
            logger.info("Database connection closed")

    async def health_check(self) -> Dict[str, Any]:
        """
        Perform health check on all connections.

        Returns:
            Dictionary with status of HTTP and database connections.
        """
        health_status = {
            "http_session": "unknown",
            "database": "unknown",
            "timestamp": asyncio.get_event_loop().time()
        }

        # Check HTTP session
        try:
            if self._http_session and not self._http_session.closed:
                # Simple ping to check connectivity
                async with self._http_session.get("/api/v1/version") as response:
                    if response.status < 400:
                        health_status["http_session"] = "healthy"
                    else:
                        health_status["http_session"] = "degraded"
            else:
                health_status["http_session"] = "disconnected"
        except Exception as e:
            health_status["http_session"] = f"error: {str(e)}"
            logger.warning(f"HTTP health check failed: {e}")

        # Check database connection
        try:
            if self._db_pool:
                self._db_pool.execute("SELECT 1").fetchone()
                health_status["database"] = "healthy"
            else:
                health_status["database"] = "disconnected"
        except Exception as e:
            health_status["database"] = f"error: {str(e)}"
            logger.warning(f"Database health check failed: {e}")

        return health_status


class RetryConfig:
    """Configuration for retry mechanisms."""

    def __init__(
        self,
        max_attempts: int = 3,
        base_delay: float = 1.0,
        backoff_factor: float = 2.0,
        max_delay: float = 60.0
    ):
        self.max_attempts = max_attempts
        self.base_delay = base_delay
        self.backoff_factor = backoff_factor
        self.max_delay = max_delay


def retry_with_backoff(retry_config: RetryConfig):
    """
    Decorator for implementing retry with exponential backoff.

    Args:
        retry_config: Configuration for retry behavior

    Returns:
        Decorator function that wraps methods with retry logic
    """
    def decorator(func):
        async def wrapper(*args, **kwargs):
            last_exception = None

            for attempt in range(retry_config.max_attempts):
                try:
                    return await func(*args, **kwargs)
                except Exception as e:
                    last_exception = e

                    if attempt == retry_config.max_attempts - 1:
                        # Last attempt, don't wait
                        break

                    # Calculate delay with exponential backoff
                    delay = min(
                        retry_config.base_delay * (retry_config.backoff_factor ** attempt),
                        retry_config.max_delay
                    )

                    logger.warning(
                        f"Attempt {attempt + 1}/{retry_config.max_attempts} failed for {func.__name__}: {e}. "
                        f"Retrying in {delay:.1f}s"
                    )

                    await asyncio.sleep(delay)

            # All attempts failed
            logger.error(f"All {retry_config.max_attempts} attempts failed for {func.__name__}")
            raise last_exception

        return wrapper
    return decorator