import os

from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase

from adapters.mongo_availability_repository import MongoAvailabilityRepository
from adapters.mongo_booking_repository import MongoBookingRepository
from adapters.mongo_client_repository import MongoClientRepository
from adapters.mongo_salon_repository import MongoSalonRepository
from adapters.mongo_service_repository import MongoServiceRepository
from app_resources import AppResources
from tools.book_appointment import BookAppointmentTool
from tools.get_service import GetServiceTool
from tools.get_service_availability import GetServiceAvailabilityTool
from use_cases.book_appointment_permissive import BookAppointmentPermissiveUseCase
from use_cases.book_appointments import BookAppointmentsUseCase
from use_cases.enrich_call_context import EnrichCallContextUseCase
from use_cases.get_eligible_offers import GetEligibleOffersUseCase
from use_cases.get_service_availability import GetServiceAvailabilityUseCase
from use_cases.get_service_catalog import GetServiceCatalogUseCase
from use_cases.resolve_offer_by_title import ResolveOfferByTitleUseCase
from use_cases.resolve_salon_id import ResolveSalonIdUseCase
from use_cases.resolve_services_by_names import ResolveServicesByNamesUseCase


_mongo_client: AsyncIOMotorClient | None = None


def _get_db() -> AsyncIOMotorDatabase:
    global _mongo_client
    mongo_uri = os.getenv("MONGO_URI")
    if not mongo_uri:
        raise ValueError("MONGO_URI environment variable is required")

    db_name = os.getenv("MONGO_DB_NAME", "SmartReceptionist")
    if _mongo_client is None:
        _mongo_client = AsyncIOMotorClient(mongo_uri)
    return _mongo_client[db_name]


def _service_repository() -> MongoServiceRepository:
    db = _get_db()
    return MongoServiceRepository(
        service_collection=db["services"],
        stylist_collection=db["stylists"],
    )


def build_app_resources(access_token: str | None = None) -> AppResources:
    if access_token:
        salon_id = ResolveSalonIdUseCase().execute(access_token)
    else:
        salon_id = os.getenv("SALON_ID")
        if not salon_id:
            raise ValueError(
                "access_token stream parameter or SALON_ID environment variable is required"
            )
    return AppResources(salon_id=salon_id)


def build_enrich_call_context() -> EnrichCallContextUseCase:
    db = _get_db()
    return EnrichCallContextUseCase(
        client_repository=MongoClientRepository(db["clients"]),
        salon_repository=MongoSalonRepository(db["salons"]),
    )


def build_get_service_tool() -> GetServiceTool:
    use_case = GetServiceCatalogUseCase(_service_repository())
    return GetServiceTool(use_case)


def build_get_service_availability_tool() -> GetServiceAvailabilityTool:
    db = _get_db()
    service_repository = _service_repository()
    availability_repository = MongoAvailabilityRepository(
        service_collection=db["services"],
        salon_collection=db["salons"],
        stylist_collection=db["stylists"],
        appointment_collection=db["appointments"],
    )
    use_case = GetServiceAvailabilityUseCase(
        ResolveServicesByNamesUseCase(service_repository),
        availability_repository,
    )
    return GetServiceAvailabilityTool(use_case)


def build_book_appointment_tool() -> BookAppointmentTool:
    db = _get_db()
    service_repository = _service_repository()
    booking_repository = MongoBookingRepository(
        appointment_collection=db["appointments"],
        offer_collection=db["offers"],
        stylist_collection=db["stylists"],
        service_collection=db["services"],
    )
    salon_repository = MongoSalonRepository(db["salons"])

    resolve_services = ResolveServicesByNamesUseCase(service_repository)
    get_eligible_offers = GetEligibleOffersUseCase(booking_repository)
    resolve_offer = ResolveOfferByTitleUseCase(get_eligible_offers)
    book_permissive = BookAppointmentPermissiveUseCase(
        booking_repository, salon_repository
    )
    book_appointments = BookAppointmentsUseCase(
        resolve_services, resolve_offer, book_permissive
    )
    return BookAppointmentTool(book_appointments)
