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

This module handles all database operations for infrastructure assets, 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.infrastructure import Infrastructure
from api.schemas.infrastructure import InfrastructureCreate, InfrastructureUpdate


def get_infrastructure_items(db: Session, skip: int = 0, limit: int = 100) -> tuple[list[Infrastructure], int]:
    """
    Retrieve a paginated list of infrastructure items.

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

    Returns:
        tuple: (list of infrastructure items, total count)

    Example:
        ```python
        items, total = get_infrastructure_items(db, skip=0, limit=50)
        print(f"Retrieved {len(items)} of {total} infrastructure items")
        ```
    """
    # Get total count
    total = db.query(Infrastructure).count()

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

    return items, total


def get_infrastructure_by_id(db: Session, infrastructure_id: UUID) -> Infrastructure:
    """
    Retrieve a single infrastructure item by its ID.

    Args:
        db: Database session
        infrastructure_id: UUID of the infrastructure item to retrieve

    Returns:
        Infrastructure: The infrastructure object

    Raises:
        HTTPException: 404 if infrastructure not found

    Example:
        ```python
        item = get_infrastructure_by_id(db, infrastructure_id)
        print(f"Found infrastructure: {item.hostname}")
        ```
    """
    item = db.query(Infrastructure).filter(Infrastructure.id == str(infrastructure_id)).first()

    if not item:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Infrastructure with ID {infrastructure_id} not found"
        )

    return item


def get_infrastructure_by_site(db: Session, site_id: str, skip: int = 0, limit: int = 100) -> tuple[list[Infrastructure], int]:
    """
    Retrieve infrastructure items for a specific site.

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

    Returns:
        tuple: (list of infrastructure items, total count)

    Example:
        ```python
        items, total = get_infrastructure_by_site(db, site_id, skip=0, limit=50)
        print(f"Retrieved {len(items)} of {total} items for site")
        ```
    """
    # Get total count for this site
    total = db.query(Infrastructure).filter(Infrastructure.site_id == site_id).count()

    # Get paginated results
    items = (
        db.query(Infrastructure)
        .filter(Infrastructure.site_id == site_id)
        .order_by(Infrastructure.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return items, total


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

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

    Returns:
        tuple: (list of infrastructure items, total count)

    Example:
        ```python
        items, total = get_infrastructure_by_client(db, client_id, skip=0, limit=50)
        print(f"Retrieved {len(items)} of {total} items for client")
        ```
    """
    # Get total count for this client
    total = db.query(Infrastructure).filter(Infrastructure.client_id == client_id).count()

    # Get paginated results
    items = (
        db.query(Infrastructure)
        .filter(Infrastructure.client_id == client_id)
        .order_by(Infrastructure.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return items, total


def get_infrastructure_by_type(db: Session, infra_type: str, skip: int = 0, limit: int = 100) -> tuple[list[Infrastructure], int]:
    """
    Retrieve infrastructure items by asset type.

    Args:
        db: Database session
        infra_type: Asset type to filter by
        skip: Number of records to skip (for pagination)
        limit: Maximum number of records to return

    Returns:
        tuple: (list of infrastructure items, total count)

    Example:
        ```python
        items, total = get_infrastructure_by_type(db, "physical_server", skip=0, limit=50)
        print(f"Retrieved {len(items)} of {total} physical servers")
        ```
    """
    # Get total count for this type
    total = db.query(Infrastructure).filter(Infrastructure.asset_type == infra_type).count()

    # Get paginated results
    items = (
        db.query(Infrastructure)
        .filter(Infrastructure.asset_type == infra_type)
        .order_by(Infrastructure.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return items, total


def create_infrastructure(db: Session, infrastructure_data: InfrastructureCreate) -> Infrastructure:
    """
    Create a new infrastructure item.

    Args:
        db: Database session
        infrastructure_data: Infrastructure creation data

    Returns:
        Infrastructure: The created infrastructure object

    Raises:
        HTTPException: 409 if validation fails
        HTTPException: 422 if foreign key validation fails
        HTTPException: 500 if database error occurs

    Example:
        ```python
        infra_data = InfrastructureCreate(
            hostname="server-01",
            asset_type="physical_server",
            client_id="client-uuid"
        )
        infra = create_infrastructure(db, infra_data)
        print(f"Created infrastructure: {infra.id}")
        ```
    """
    # Validate foreign keys if provided
    if infrastructure_data.client_id:
        from api.models.client import Client
        client = db.query(Client).filter(Client.id == infrastructure_data.client_id).first()
        if not client:
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail=f"Client with ID {infrastructure_data.client_id} not found"
            )

    if infrastructure_data.site_id:
        from api.models.site import Site
        site = db.query(Site).filter(Site.id == infrastructure_data.site_id).first()
        if not site:
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail=f"Site with ID {infrastructure_data.site_id} not found"
            )

    if infrastructure_data.parent_host_id:
        parent = db.query(Infrastructure).filter(Infrastructure.id == infrastructure_data.parent_host_id).first()
        if not parent:
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail=f"Parent host with ID {infrastructure_data.parent_host_id} not found"
            )

    try:
        # Create new infrastructure instance
        db_infrastructure = Infrastructure(**infrastructure_data.model_dump())

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

        return db_infrastructure

    except IntegrityError as e:
        db.rollback()
        # Handle constraint violations
        if "client_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail=f"Invalid client_id: {infrastructure_data.client_id}"
            )
        elif "site_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail=f"Invalid site_id: {infrastructure_data.site_id}"
            )
        elif "parent_host_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail=f"Invalid parent_host_id: {infrastructure_data.parent_host_id}"
            )
        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 infrastructure: {str(e)}"
        )


def update_infrastructure(db: Session, infrastructure_id: UUID, infrastructure_data: InfrastructureUpdate) -> Infrastructure:
    """
    Update an existing infrastructure item.

    Args:
        db: Database session
        infrastructure_id: UUID of the infrastructure item to update
        infrastructure_data: Infrastructure update data (only provided fields will be updated)

    Returns:
        Infrastructure: The updated infrastructure object

    Raises:
        HTTPException: 404 if infrastructure not found
        HTTPException: 422 if foreign key validation fails
        HTTPException: 500 if database error occurs

    Example:
        ```python
        update_data = InfrastructureUpdate(
            status="decommissioned",
            notes="Server retired"
        )
        infra = update_infrastructure(db, infrastructure_id, update_data)
        print(f"Updated infrastructure: {infra.hostname}")
        ```
    """
    # Get existing infrastructure
    infrastructure = get_infrastructure_by_id(db, infrastructure_id)

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

        # Validate foreign keys if being updated
        if "client_id" in update_data and update_data["client_id"]:
            from api.models.client import Client
            client = db.query(Client).filter(Client.id == update_data["client_id"]).first()
            if not client:
                raise HTTPException(
                    status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                    detail=f"Client with ID {update_data['client_id']} not found"
                )

        if "site_id" in update_data and update_data["site_id"]:
            from api.models.site import Site
            site = db.query(Site).filter(Site.id == update_data["site_id"]).first()
            if not site:
                raise HTTPException(
                    status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                    detail=f"Site with ID {update_data['site_id']} not found"
                )

        if "parent_host_id" in update_data and update_data["parent_host_id"]:
            parent = db.query(Infrastructure).filter(Infrastructure.id == update_data["parent_host_id"]).first()
            if not parent:
                raise HTTPException(
                    status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                    detail=f"Parent host with ID {update_data['parent_host_id']} not found"
                )

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

        db.commit()
        db.refresh(infrastructure)

        return infrastructure

    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        if "client_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid client_id"
            )
        elif "site_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid site_id"
            )
        elif "parent_host_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid parent_host_id"
            )
        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 infrastructure: {str(e)}"
        )


def delete_infrastructure(db: Session, infrastructure_id: UUID) -> dict:
    """
    Delete an infrastructure item by its ID.

    Args:
        db: Database session
        infrastructure_id: UUID of the infrastructure item to delete

    Returns:
        dict: Success message

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

    Example:
        ```python
        result = delete_infrastructure(db, infrastructure_id)
        print(result["message"])  # "Infrastructure deleted successfully"
        ```
    """
    # Get existing infrastructure (raises 404 if not found)
    infrastructure = get_infrastructure_by_id(db, infrastructure_id)

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

        return {
            "message": "Infrastructure deleted successfully",
            "infrastructure_id": str(infrastructure_id)
        }

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