"""
Service service layer for business logic and database operations.

This module handles all database operations for services, providing a clean
separation between the API routes and data access layer.
"""

from typing import Optional
from uuid import UUID

from fastapi import HTTPException, status
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from api.models.service import Service
from api.models.infrastructure import Infrastructure
from api.schemas.service import ServiceCreate, ServiceUpdate


def get_services(db: Session, skip: int = 0, limit: int = 100) -> tuple[list[Service], int]:
    """
    Retrieve a paginated list of services.

    Args:
        db: Database session
        skip: Number of records to skip (for pagination)
        limit: Maximum number of records to return

    Returns:
        tuple: (list of services, total count)

    Example:
        ```python
        services, total = get_services(db, skip=0, limit=50)
        print(f"Retrieved {len(services)} of {total} services")
        ```
    """
    # Get total count
    total = db.query(Service).count()

    # Get paginated results, ordered by created_at descending (newest first)
    services = (
        db.query(Service)
        .order_by(Service.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return services, total


def get_service_by_id(db: Session, service_id: UUID) -> Service:
    """
    Retrieve a single service by its ID.

    Args:
        db: Database session
        service_id: UUID of the service to retrieve

    Returns:
        Service: The service object

    Raises:
        HTTPException: 404 if service not found

    Example:
        ```python
        service = get_service_by_id(db, service_id)
        print(f"Found service: {service.service_name}")
        ```
    """
    service = db.query(Service).filter(Service.id == str(service_id)).first()

    if not service:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Service with ID {service_id} not found"
        )

    return service


def get_services_by_client(db: Session, client_id: str, skip: int = 0, limit: int = 100) -> tuple[list[Service], int]:
    """
    Retrieve services for a specific client (via infrastructure).

    Args:
        db: Database session
        client_id: Client UUID
        skip: Number of records to skip
        limit: Maximum number of records to return

    Returns:
        tuple: (list of services, total count)

    Example:
        ```python
        services, total = get_services_by_client(db, client_id)
        print(f"Client has {total} services")
        ```
    """
    # Join with Infrastructure to filter by client_id
    query = (
        db.query(Service)
        .join(Infrastructure, Service.infrastructure_id == Infrastructure.id)
        .filter(Infrastructure.client_id == str(client_id))
    )

    total = query.count()

    services = (
        query
        .order_by(Service.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return services, total


def get_services_by_type(db: Session, service_type: str, skip: int = 0, limit: int = 100) -> tuple[list[Service], int]:
    """
    Retrieve services by type.

    Args:
        db: Database session
        service_type: Service type to filter by (e.g., 'git_hosting', 'database')
        skip: Number of records to skip
        limit: Maximum number of records to return

    Returns:
        tuple: (list of services, total count)

    Example:
        ```python
        services, total = get_services_by_type(db, "database")
        print(f"Found {total} database services")
        ```
    """
    total = db.query(Service).filter(Service.service_type == service_type).count()

    services = (
        db.query(Service)
        .filter(Service.service_type == service_type)
        .order_by(Service.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return services, total


def get_services_by_status(db: Session, status_filter: str, skip: int = 0, limit: int = 100) -> tuple[list[Service], int]:
    """
    Retrieve services by status.

    Args:
        db: Database session
        status_filter: Status to filter by (running, stopped, error, maintenance)
        skip: Number of records to skip
        limit: Maximum number of records to return

    Returns:
        tuple: (list of services, total count)

    Example:
        ```python
        services, total = get_services_by_status(db, "running")
        print(f"Found {total} running services")
        ```
    """
    total = db.query(Service).filter(Service.status == status_filter).count()

    services = (
        db.query(Service)
        .filter(Service.status == status_filter)
        .order_by(Service.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return services, total


def validate_infrastructure_exists(db: Session, infrastructure_id: str) -> None:
    """
    Validate that infrastructure exists.

    Args:
        db: Database session
        infrastructure_id: Infrastructure UUID to validate

    Raises:
        HTTPException: 404 if infrastructure not found

    Example:
        ```python
        validate_infrastructure_exists(db, infrastructure_id)
        # Continues if infrastructure exists, raises HTTPException if not
        ```
    """
    infrastructure = db.query(Infrastructure).filter(Infrastructure.id == str(infrastructure_id)).first()
    if not infrastructure:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Infrastructure with ID {infrastructure_id} not found"
        )


def create_service(db: Session, service_data: ServiceCreate) -> Service:
    """
    Create a new service.

    Args:
        db: Database session
        service_data: Service creation data

    Returns:
        Service: The created service object

    Raises:
        HTTPException: 404 if infrastructure not found
        HTTPException: 500 if database error occurs

    Example:
        ```python
        service_data = ServiceCreate(
            infrastructure_id="123e4567-e89b-12d3-a456-426614174000",
            service_name="Gitea",
            service_type="git_hosting",
            status="running"
        )
        service = create_service(db, service_data)
        print(f"Created service: {service.id}")
        ```
    """
    # Validate infrastructure exists if provided
    if service_data.infrastructure_id:
        validate_infrastructure_exists(db, service_data.infrastructure_id)

    try:
        # Create new service instance
        db_service = Service(**service_data.model_dump())

        # Add to database
        db.add(db_service)
        db.commit()
        db.refresh(db_service)

        return db_service

    except IntegrityError as e:
        db.rollback()
        # Handle foreign key constraint violations
        if "infrastructure_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Infrastructure with ID {service_data.infrastructure_id} not found"
            )
        else:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Database error: {str(e)}"
            )
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to create service: {str(e)}"
        )


def update_service(db: Session, service_id: UUID, service_data: ServiceUpdate) -> Service:
    """
    Update an existing service.

    Args:
        db: Database session
        service_id: UUID of the service to update
        service_data: Service update data (only provided fields will be updated)

    Returns:
        Service: The updated service object

    Raises:
        HTTPException: 404 if service or infrastructure not found
        HTTPException: 500 if database error occurs

    Example:
        ```python
        update_data = ServiceUpdate(
            status="stopped",
            notes="Service temporarily stopped for maintenance"
        )
        service = update_service(db, service_id, update_data)
        print(f"Updated service: {service.service_name}")
        ```
    """
    # Get existing service
    service = get_service_by_id(db, service_id)

    try:
        # Update only provided fields
        update_data = service_data.model_dump(exclude_unset=True)

        # If updating infrastructure_id, validate infrastructure exists
        if "infrastructure_id" in update_data and update_data["infrastructure_id"] and update_data["infrastructure_id"] != service.infrastructure_id:
            validate_infrastructure_exists(db, update_data["infrastructure_id"])

        # Apply updates
        for field, value in update_data.items():
            setattr(service, field, value)

        db.commit()
        db.refresh(service)

        return service

    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        if "infrastructure_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Infrastructure not found"
            )
        else:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Database error: {str(e)}"
            )
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to update service: {str(e)}"
        )


def delete_service(db: Session, service_id: UUID) -> dict:
    """
    Delete a service by its ID.

    Args:
        db: Database session
        service_id: UUID of the service to delete

    Returns:
        dict: Success message

    Raises:
        HTTPException: 404 if service not found
        HTTPException: 500 if database error occurs

    Example:
        ```python
        result = delete_service(db, service_id)
        print(result["message"])  # "Service deleted successfully"
        ```
    """
    # Get existing service (raises 404 if not found)
    service = get_service_by_id(db, service_id)

    try:
        db.delete(service)
        db.commit()

        return {
            "message": "Service deleted successfully",
            "service_id": str(service_id)
        }

    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to delete service: {str(e)}"
        )
