from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from uuid import UUID

from src.apps.base.models.locations import Location, Locale
from src.apps.base.schemas.locations import (
    LocationCreateSchema, LocationUpdateSchema,
    LocaleCreateSchema, LocaleUpdateSchema, LocationOutSchema, LocaleOutSchema, LocationFilterSchema, LocaleFilterSchema
)
from src.utils.pagination import QueryPaginator
from src.apps.base.models.regions import Region
from src.core.config import settings
from src.utils.constants import API_PREFIXES
from src.core.exceptions import APIException
from src.apps.base.services.region import get_region_by_id


# ---------------- Location Services ----------------

async def get_all_locations(db: Session, page: int = 1, per_page: int = 10, payload: LocationFilterSchema = None):
    try:
        offset = (page - 1) * per_page
        query = db.query(Location).filter(
            Location.deleted_at.is_(None)
        ).order_by(Location.created_at.desc())
        
        if payload and payload.region_id:
            region = await get_region_by_id(db, payload.region_id)
            query = query.filter(Location.region_id == region.id)
            
        paginator = QueryPaginator(
            query=query,
            schema=LocationOutSchema,
            url="".join([str(settings.api_base_url()), API_PREFIXES.LOCATION]),
            offset=offset,
            limit=per_page,
            use_orm=True
        )
        return paginator.paginate()
    except Exception as e:
        raise APIException(
            module="get_all_locations",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Failed to fetch locations."
        )


async def get_location_by_id(db: Session, location_id: int):
    try:
        location = db.query(Location).filter(
            Location.id == location_id,
            Location.deleted_at.is_(None)
        ).first()
        if not location:
            raise APIException(
                module="Location",
                error={"exception": "Location not found"},
                status_code=status.HTTP_404_NOT_FOUND,
                message="Location not found."
            )
        return location
    except Exception as e:
        raise APIException(
            module="Location",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error fetching location by ID."
        )


async def create_location_with_locales(db: Session, payload: LocationCreateSchema):
    # Validate region exists
    region = await get_region_by_id(db, payload.region_id)

    # Check for duplicate location in region
    existing_location = db.query(Location).filter(
        Location.name == payload.name,
        Location.region_id == region.id
    ).first()
    if existing_location:
        raise APIException(
            module="Location",
            error={"exception": "Location already exists in this region"},
            status_code=status.HTTP_400_BAD_REQUEST,
            message="Location already exists in this region."
        )

    # ✅ Create location using region_id
    try:
        location = Location(
            name=payload.name,
            region_id=region.id
        )
        db.add(location)
        db.flush()  # to get location.id or location.uuid before commit
        db.refresh(location)
    except Exception as e:
        raise APIException(
            module="Location",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error creating location."
        )

    if payload.locales:
        for locale_payload in payload.locales:
            existing_locale = db.query(Locale).filter(
                Locale.name == locale_payload.name,
                Locale.location_id == location.id,
                Locale.deleted_at.is_(None)
            ).first()
            if existing_locale:
                continue # Skip if locale already exists
            try:
                locale = Locale(name=locale_payload.name, location_id=location.id)
                db.add(locale)
            except Exception as e:
                raise APIException(
                    module="Locale",
                    error={"exception": str(e)},
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    message="Error creating locale."
                )
    try:
        db.commit()
        db.refresh(location)
        return location
    except Exception as e:
        db.rollback()
        raise APIException(
            module="Location",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error committing location creation."
        )


async def update_location(db: Session, location_id: int, payload: LocationUpdateSchema):
    location = await get_location_by_id(db, location_id)
    try:
        if payload.name is not None:
            location.name = payload.name

        if payload.region_id is not None:
            region = await get_region_by_id(db, payload.region_id)
            location.region_id = region.id

        if payload.locales is not None:
            # db.query(Locale).filter(Locale.location_id == location.id).delete()
            for locale_payload in payload.locales:
                try:
                    existing_locale = db.query(Locale).filter(
                        Locale.name == locale_payload.name,
                        Locale.location_id == location.id,
                        Locale.deleted_at.is_(None)
                    ).first()
                    if existing_locale:
                        continue
                except Exception as e:
                    raise APIException(
                        module="Locale",
                        error={"exception": str(e)},
                        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                        message="Error checking existing locale."
                    )
                try:
                    locale = Locale(
                        name=locale_payload.name, 
                        location_id=location.id
                    )
                    db.add(locale)
                except Exception as e:
                    raise APIException(
                        module="Locale",
                        error={"exception": str(e)},
                        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                        message="Error creating locale."
                    )
    
        db.commit()
        db.refresh(location)
        return location
    except Exception as e:
        db.rollback()
        raise APIException(
            module="Location",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error updating location."
        )


async def delete_location(db: Session, location_id: int):
    location = await get_location_by_id(db, location_id)
    try:
        location.deleted_at = datetime.now(timezone.utc)
        db.commit()
        db.refresh(location)
        return None
    except Exception as e:
        db.rollback()
        raise APIException(
            module="Location",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error deleting location."
        )


# ---------------- Locale Services ----------------

async def get_all_locales(
    db: Session, 
    payload: LocaleFilterSchema = None,
    page: int = 1, 
    per_page: int = 10
):
    try:
        offset = (page - 1) * per_page
        query = db.query(Locale).filter(
            Locale.deleted_at.is_(None)
        ).order_by(Locale.created_at.desc())
        
        if payload and payload.location_id:
            location = await get_location_by_id(db, payload.location_id)
            query = query.filter(Locale.location_id == location.id)

        paginator = QueryPaginator(
            query=query,
            schema=LocaleOutSchema,
            url="".join([str(settings.api_base_url()), API_PREFIXES.LOCALE]),
            offset=offset,
            limit=per_page,
            use_orm=True
        )
        return paginator.paginate()
    except Exception as e:
        raise APIException(
            module="get_all_locales",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Failed to fetch locales."
        )


async def get_locale_by_id(db: Session, locale_id: int):
    try:
        locale = db.query(Locale).filter(
            Locale.id == locale_id,
            Locale.deleted_at.is_(None)
        ).first()
        if not locale:
            raise APIException(
                module="Locale",
                error={"exception": "Locale not found"},
                status_code=status.HTTP_404_NOT_FOUND,
                message="Locale not found."
            )
        return locale
    except Exception as e:
        raise APIException(
            module="Locale",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error fetching locale by ID."
        )



async def create_locale(db: Session, payload: LocaleCreateSchema):
    location = await get_location_by_id(db, payload.location_id)
    try:
        existing_locale = db.query(Locale).filter(
            Locale.name == payload.name,
            Locale.location_id == location.id,
            Locale.deleted_at.is_(None)
        ).first()
        if existing_locale:
            raise APIException(
                module="Locale",
                error={"exception": "Locale already exists in this location"},
                status_code=status.HTTP_400_BAD_REQUEST,
                message="Locale already exists in this location."
            )
    except Exception as e:
        raise APIException(
            module="Locale",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error checking existing locale."
        )
    try:
        locale = Locale(name=payload.name, location_id=location.id)
        db.add(locale)
        db.commit()
        db.refresh(locale)
        return locale
    except Exception as e:
        db.rollback()
        raise APIException(
            module="Locale",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error creating locale."
        )


async def update_locale(db: Session, locale_id: int, payload: LocaleUpdateSchema):
    locale = await get_locale_by_id(db, locale_id)
    try:
        if payload.name is not None:
            locale.name = payload.name
        if payload.location_id is not None:
            location = await get_location_by_id(db, payload.location_id)
            locale.location_id = location.id

        db.commit()
        db.refresh(locale)
        return locale
    except Exception as e:
        db.rollback()
        raise APIException(
            module="Locale",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error updating locale."
        )


async def delete_locale(db: Session, locale_id: int):
    locale = await get_locale_by_id(db, locale_id)
    try:
        db.delete(locale)
        db.commit()
        return None
    except Exception as e:
        db.rollback()
        raise APIException(
            module="Locale",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Error deleting locale."
        )
