"""Service layer for business logic."""

import asyncio
import logging
import traceback
from typing import List, Optional

from .config import MonitorConfig
from .exceptions import ValidationError
from .models import (
    AnalyticsData,
    ErrorInfo,
    MonitorRecord,
    PaginationInfo,
    RequestData,
    ResponseData,
    StatsData,
)
from .repository import MonitorRepository

logger = logging.getLogger(__name__)


class MonitorService:
    """Service for monitoring operations."""

    def __init__(self, repository: MonitorRepository, config: MonitorConfig):
        self.repository = repository
        self.config = config
        self._cleanup_task: Optional[asyncio.Task] = None

    async def start_background_tasks(self) -> None:
        """Start background tasks."""
        if self.config.cleanup_interval > 0:
            self._cleanup_task = asyncio.create_task(self._periodic_cleanup())
            logger.info("Started background cleanup task")

    async def stop_background_tasks(self) -> None:
        """Stop background tasks."""
        if self._cleanup_task:
            self._cleanup_task.cancel()
            try:
                await self._cleanup_task
            except asyncio.CancelledError:
                pass
            logger.info("Stopped background cleanup task")

    async def _periodic_cleanup(self) -> None:
        """Periodic cleanup of old records."""
        while True:
            try:
                await asyncio.sleep(self.config.cleanup_interval)
                deleted_count = await self.repository.cleanup_old_records(self.config.max_records)
                if deleted_count > 0:
                    logger.info(f"Cleaned up {deleted_count} old records")
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Error during cleanup: {e}")

    async def record_request(
        self,
        request_data: RequestData,
        response_data: ResponseData,
        error_info: Optional[ErrorInfo] = None
    ) -> None:
        """Record a request/response pair."""
        try:
            # Validate data sizes
            if (request_data.request_body and
                len(request_data.request_body.encode()) > self.config.max_request_body_size):
                request_data.request_body = f"[Body too large: {len(request_data.request_body.encode())} bytes]"

            if (response_data.response_body and
                len(response_data.response_body.encode()) > self.config.max_response_body_size):
                response_data.response_body = f"[Body too large: {len(response_data.response_body.encode())} bytes]"

            record = MonitorRecord(
                request=request_data,
                response=response_data,
                error_info=error_info
            )

            await self.repository.store_record(record)
            logger.debug(f"Recorded request: {request_data.method} {request_data.path}")

        except Exception as e:
            logger.error(f"Failed to record request: {e}")
            # Don't re-raise to avoid breaking the main application

    async def get_recent_requests(self, page: int = 1, page_size: int = 20) -> tuple[List[MonitorRecord], PaginationInfo]:
        """Get recent requests with pagination."""
        if page < 1:
            raise ValidationError("Page number must be >= 1")
        if page_size < 1 or page_size > 100:
            raise ValidationError("Page size must be between 1 and 100")

        offset = (page - 1) * page_size
        records = await self.repository.get_records(page_size, offset)

        # Get total count for pagination
        stats = await self.repository.get_stats()
        total_pages = (stats.total_requests + page_size - 1) // page_size

        pagination = PaginationInfo(
            current_page=page,
            total_pages=max(1, total_pages),
            total_records=stats.total_requests,
            page_size=page_size
        )

        return records, pagination

    async def get_request_details(self, request_id: int) -> Optional[MonitorRecord]:
        """Get detailed information for a specific request."""
        if request_id < 1:
            raise ValidationError("Request ID must be >= 1")

        return await self.repository.get_record_by_id(request_id)

    async def get_statistics(self) -> StatsData:
        """Get monitoring statistics."""
        return await self.repository.get_stats()

    async def get_analytics_data(self, resolution: str = "30s") -> AnalyticsData:
        """Get analytics data for dashboard."""
        valid_resolutions = ["30s", "1m", "5m", "15m", "30m", "1h", "1d"]
        if resolution not in valid_resolutions:
            raise ValidationError(f"Invalid resolution. Must be one of: {valid_resolutions}")

        return await self.repository.get_analytics(resolution)

    def should_monitor_path(self, path: str) -> bool:
        """Check if a path should be monitored."""
        return not any(path.startswith(excluded) for excluded in self.config.exclude_paths)


class RequestProcessor:
    """Processes HTTP requests for monitoring."""

    def __init__(self, config: MonitorConfig):
        self.config = config

    def extract_request_data(self, request, start_time: float, request_body: Optional[str] = None) -> RequestData:
        """Extract request data from FastAPI request."""
        return RequestData(
            method=request.method,
            path=str(request.url.path),
            query_params=str(request.url.query) if request.url.query else None,
            headers=dict(request.headers),
            client_ip=request.client.host if request.client else None,
            user_agent=request.headers.get("user-agent"),
            request_body=request_body,
            request_size=len(request_body.encode()) if request_body else 0,
            timestamp=start_time
        )

    def extract_response_data(self, response, response_time: float, response_body: Optional[str] = None) -> ResponseData:
        """Extract response data from FastAPI response."""
        return ResponseData(
            status_code=response.status_code,
            response_time=response_time,
            response_size=len(response_body.encode()) if response_body else 0,
            response_headers=dict(response.headers),
            response_body=response_body
        )

    def extract_error_info(self, exception: Exception) -> ErrorInfo:
        """Extract error information from exception."""
        return ErrorInfo(
            error_type=type(exception).__name__,
            error_message=str(exception),
            traceback=traceback.format_exc()
        )

    async def capture_request_body(self, request) -> Optional[str]:
        """Safely capture request body."""
        if request.method not in ["POST", "PUT", "PATCH"]:
            return None

        try:
            body = await request.body()
            if not body:
                return None

            # Check size limit
            if len(body) > self.config.max_request_body_size:
                return f"[Body too large: {len(body)} bytes]"

            return body.decode("utf-8")
        except Exception as e:
            logger.warning(f"Failed to capture request body: {e}")
            return f"[Failed to capture body: {e}]"

    def capture_response_body(self, response_body: bytes) -> Optional[str]:
        """Safely capture response body."""
        if not response_body:
            return None

        try:
            # Check size limit
            if len(response_body) > self.config.max_response_body_size:
                return f"[Body too large: {len(response_body)} bytes]"

            return response_body.decode("utf-8")
        except Exception as e:
            logger.warning(f"Failed to capture response body: {e}")
            return f"[Failed to capture body: {e}]"
