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

This module handles all database operations for sites, 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.site import Site
from api.models.client import Client
from api.schemas.site import SiteCreate, SiteUpdate


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

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

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

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

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

    return sites, total


def get_site_by_id(db: Session, site_id: UUID) -> Site:
    """
    Retrieve a single site by its ID.

    Args:
        db: Database session
        site_id: UUID of the site to retrieve

    Returns:
        Site: The site object

    Raises:
        HTTPException: 404 if site not found

    Example:
        ```python
        site = get_site_by_id(db, site_id)
        print(f"Found site: {site.name}")
        ```
    """
    site = db.query(Site).filter(Site.id == str(site_id)).first()

    if not site:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Site with ID {site_id} not found"
        )

    return site


def get_sites_by_client(db: Session, client_id: UUID, skip: int = 0, limit: int = 100) -> tuple[list[Site], int]:
    """
    Retrieve sites belonging to 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 sites, total count for this client)

    Raises:
        HTTPException: 404 if client not found

    Example:
        ```python
        sites, total = get_sites_by_client(db, client_id, skip=0, limit=50)
        print(f"Retrieved {len(sites)} of {total} sites for client")
        ```
    """
    # Verify client exists
    client = db.query(Client).filter(Client.id == str(client_id)).first()
    if not client:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Client with ID {client_id} not found"
        )

    # Get total count for this client
    total = db.query(Site).filter(Site.client_id == str(client_id)).count()

    # Get paginated results
    sites = (
        db.query(Site)
        .filter(Site.client_id == str(client_id))
        .order_by(Site.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return sites, total


def create_site(db: Session, site_data: SiteCreate) -> Site:
    """
    Create a new site.

    Args:
        db: Database session
        site_data: Site creation data

    Returns:
        Site: The created site object

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

    Example:
        ```python
        site_data = SiteCreate(
            client_id="123e4567-e89b-12d3-a456-426614174000",
            name="Main Office",
            network_subnet="172.16.9.0/24"
        )
        site = create_site(db, site_data)
        print(f"Created site: {site.id}")
        ```
    """
    # Verify client exists
    client = db.query(Client).filter(Client.id == str(site_data.client_id)).first()
    if not client:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Client with ID {site_data.client_id} not found"
        )

    try:
        # Create new site instance
        db_site = Site(**site_data.model_dump())

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

        return db_site

    except IntegrityError as e:
        db.rollback()
        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 site: {str(e)}"
        )


def update_site(db: Session, site_id: UUID, site_data: SiteUpdate) -> Site:
    """
    Update an existing site.

    Args:
        db: Database session
        site_id: UUID of the site to update
        site_data: Site update data (only provided fields will be updated)

    Returns:
        Site: The updated site object

    Raises:
        HTTPException: 404 if site or client not found
        HTTPException: 500 if database error occurs

    Example:
        ```python
        update_data = SiteUpdate(
            name="Main Office - Renovated",
            vpn_required=True
        )
        site = update_site(db, site_id, update_data)
        print(f"Updated site: {site.name}")
        ```
    """
    # Get existing site
    site = get_site_by_id(db, site_id)

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

        # If updating client_id, verify new client exists
        if "client_id" in update_data:
            client = db.query(Client).filter(Client.id == str(update_data["client_id"])).first()
            if not client:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Client with ID {update_data['client_id']} not found"
                )

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

        db.commit()
        db.refresh(site)

        return site

    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        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 site: {str(e)}"
        )


def delete_site(db: Session, site_id: UUID) -> dict:
    """
    Delete a site by its ID.

    Args:
        db: Database session
        site_id: UUID of the site to delete

    Returns:
        dict: Success message

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

    Example:
        ```python
        result = delete_site(db, site_id)
        print(result["message"])  # "Site deleted successfully"
        ```
    """
    # Get existing site (raises 404 if not found)
    site = get_site_by_id(db, site_id)

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

        return {
            "message": "Site deleted successfully",
            "site_id": str(site_id)
        }

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