from datetime import datetime

from bson import ObjectId
from motor.motor_asyncio import AsyncIOMotorCollection

from ports.booking_repository import ClientAppointmentStats


class MongoBookingRepository:
    def __init__(
        self,
        appointment_collection: AsyncIOMotorCollection,
        offer_collection: AsyncIOMotorCollection,
        stylist_collection: AsyncIOMotorCollection,
        service_collection: AsyncIOMotorCollection,
    ):
        self._appointments = appointment_collection
        self._offers = offer_collection
        self._stylists = stylist_collection
        self._services = service_collection

    async def get_client_stats(self, client_id: str) -> ClientAppointmentStats:
        pipeline = [
            {
                "$match": {
                    "clientId": ObjectId(client_id),
                    "status": "confirmed",
                }
            },
            {
                "$group": {
                    "_id": None,
                    "totalSpend": {"$sum": "$appointmentFee"},
                    "appointmentCount": {"$sum": 1},
                }
            },
        ]
        rows = await self._appointments.aggregate(pipeline).to_list(length=1)
        if not rows:
            return ClientAppointmentStats(total_spend=0.0, appointment_count=0)
        row = rows[0]
        return ClientAppointmentStats(
            total_spend=float(row.get("totalSpend") or 0),
            appointment_count=int(row.get("appointmentCount") or 0),
        )

    async def list_active_offers(self, salon_id: str) -> list[dict]:
        return await self._offers.find(
            {"salonId": ObjectId(salon_id), "isActive": True},
            {"title": 1, "description": 1, "type": 1, "startDate": 1, "endDate": 1, "visitCount": 1, "spendThreshold": 1},
        ).to_list(length=None)

    async def pick_stylist_permissive(
        self, salon_id: str, service_id: str
    ) -> str | None:
        salon_oid = ObjectId(salon_id)
        service_oid = ObjectId(service_id)

        stylists = await self._stylists.find(
            {"salonId": salon_oid, "active": True, "services": service_oid},
            {"_id": 1},
        ).to_list(length=None)

        if not stylists:
            stylists = await self._stylists.find(
                {"salonId": salon_oid, "active": True},
                {"_id": 1},
            ).to_list(length=None)

        if not stylists:
            return None
        return str(stylists[0]["_id"])

    async def get_service_for_booking(self, service_id: str) -> dict | None:
        return await self._services.find_one(
            {"_id": ObjectId(service_id)},
            {"name": 1, "duration": 1, "basePrice": 1, "salonId": 1},
        )

    async def create_appointment(
        self,
        *,
        salon_id: str,
        service_id: str,
        client_id: str,
        stylist_id: str,
        start: datetime,
        end: datetime,
        appointment_fee: float,
        offer_id: str | None,
        generated_by: str,
    ) -> str:
        doc = {
            "salonId": ObjectId(salon_id),
            "serviceId": ObjectId(service_id),
            "clientId": ObjectId(client_id),
            "stylistId": ObjectId(stylist_id),
            "offerId": ObjectId(offer_id) if offer_id else None,
            "appointmentFee": appointment_fee,
            "start": start,
            "end": end,
            "status": "pending",
            "generatedBy": generated_by,
        }
        result = await self._appointments.insert_one(doc)
        return str(result.inserted_id)
