"""SQL-backed CartService implementation.

Queries ``shared.models.market.CartItem`` — only this module may write
to cart-domain tables on behalf of other domains.
"""
from __future__ import annotations

from decimal import Decimal

from sqlalchemy import select, update, func
from sqlalchemy.ext.asyncio import AsyncSession

from shared.models.market import CartItem
from shared.contracts.dtos import CartItemDTO, CartSummaryDTO


def _item_to_dto(ci: CartItem, product: dict | None) -> CartItemDTO:
    return CartItemDTO(
        id=ci.id,
        product_id=ci.product_id,
        quantity=ci.quantity,
        product_title=product["title"] if product else None,
        product_slug=product["slug"] if product else None,
        product_image=product["image"] if product else None,
        unit_price=Decimal(str(product.get("special_price") or product.get("regular_price") or 0)) if product else None,
        market_place_id=ci.market_place_id,
    )


async def _fetch_products_map(fetch_data, product_ids: list[int]) -> dict[int, dict]:
    """Fetch product details from market service, return {id: product_dict}."""
    if not product_ids:
        return {}
    raw = await fetch_data("market", "products-by-ids",
                           params={"ids": ",".join(str(i) for i in product_ids)},
                           required=False) or []
    return {p["id"]: p for p in raw}


class SqlCartService:

    async def cart_summary(
        self, session: AsyncSession, *,
        user_id: int | None, session_id: str | None,
        page_slug: str | None = None,
    ) -> CartSummaryDTO:
        """Build a lightweight cart summary for the current identity."""
        from shared.infrastructure.data_client import fetch_data
        from shared.contracts.dtos import CalendarEntryDTO, TicketDTO, dto_from_dict

        # Resolve page filter via blog data endpoint
        page_post_id: int | None = None
        if page_slug:
            post = await fetch_data("blog", "post-by-slug",
                                    params={"slug": page_slug}, required=False)
            if post and post.get("is_page"):
                page_post_id = post["id"]

        # --- product cart ---
        cart_q = select(CartItem).where(CartItem.deleted_at.is_(None))
        if user_id is not None:
            cart_q = cart_q.where(CartItem.user_id == user_id)
        elif session_id is not None:
            cart_q = cart_q.where(CartItem.session_id == session_id)
        else:
            return CartSummaryDTO()

        if page_post_id is not None:
            mps = await fetch_data("market", "marketplaces-for-container",
                                   params={"type": "page", "id": page_post_id},
                                   required=False) or []
            mp_ids = [mp["id"] for mp in mps]
            if mp_ids:
                cart_q = cart_q.where(CartItem.market_place_id.in_(mp_ids))
            else:
                return CartSummaryDTO()

        result = await session.execute(cart_q)
        cart_items = result.scalars().all()

        products = await _fetch_products_map(
            fetch_data, list({ci.product_id for ci in cart_items}),
        )

        count = sum(ci.quantity for ci in cart_items)
        total = Decimal("0")
        for ci in cart_items:
            p = products.get(ci.product_id)
            if p:
                price = p.get("special_price") or p.get("regular_price")
                if price:
                    total += Decimal(str(price)) * ci.quantity

        # --- calendar entries via events data endpoint ---
        cal_params: dict = {}
        if user_id is not None:
            cal_params["user_id"] = user_id
        if session_id is not None:
            cal_params["session_id"] = session_id

        if page_post_id is not None:
            cal_params["page_id"] = page_post_id
            raw_entries = await fetch_data("events", "entries-for-page",
                                           params=cal_params, required=False) or []
        else:
            raw_entries = await fetch_data("events", "pending-entries",
                                           params=cal_params, required=False) or []
        cal_entries = [dto_from_dict(CalendarEntryDTO, e) for e in raw_entries]

        calendar_count = len(cal_entries)
        calendar_total = sum(Decimal(str(e.cost or 0)) for e in cal_entries if e.cost is not None)

        # --- tickets via events data endpoint ---
        if page_post_id is not None:
            raw_tickets = await fetch_data("events", "tickets-for-page",
                                           params=cal_params, required=False) or []
        else:
            tk_params = {k: v for k, v in cal_params.items() if k != "page_id"}
            raw_tickets = await fetch_data("events", "pending-tickets",
                                           params=tk_params, required=False) or []
        tickets = [dto_from_dict(TicketDTO, t) for t in raw_tickets]

        ticket_count = len(tickets)
        ticket_total = sum(Decimal(str(t.price or 0)) for t in tickets)

        items = [_item_to_dto(ci, products.get(ci.product_id)) for ci in cart_items]

        return CartSummaryDTO(
            count=count,
            total=total,
            calendar_count=calendar_count,
            calendar_total=calendar_total,
            items=items,
            ticket_count=ticket_count,
            ticket_total=ticket_total,
        )

    async def cart_items(
        self, session: AsyncSession, *,
        user_id: int | None, session_id: str | None,
    ) -> list[CartItemDTO]:
        from shared.infrastructure.data_client import fetch_data

        cart_q = select(CartItem).where(CartItem.deleted_at.is_(None))
        if user_id is not None:
            cart_q = cart_q.where(CartItem.user_id == user_id)
        elif session_id is not None:
            cart_q = cart_q.where(CartItem.session_id == session_id)
        else:
            return []

        cart_q = cart_q.order_by(CartItem.created_at.desc())
        result = await session.execute(cart_q)
        items = result.scalars().all()

        products = await _fetch_products_map(
            fetch_data, list({ci.product_id for ci in items}),
        )
        return [_item_to_dto(ci, products.get(ci.product_id)) for ci in items]

    async def adopt_cart_for_user(
        self, session: AsyncSession, user_id: int, session_id: str,
    ) -> None:
        """Adopt anonymous cart items for a logged-in user."""
        anon_result = await session.execute(
            select(CartItem).where(
                CartItem.deleted_at.is_(None),
                CartItem.user_id.is_(None),
                CartItem.session_id == session_id,
            )
        )
        anon_items = anon_result.scalars().all()

        if anon_items:
            # Soft-delete existing user cart
            await session.execute(
                update(CartItem)
                .where(CartItem.deleted_at.is_(None), CartItem.user_id == user_id)
                .values(deleted_at=func.now())
            )
            for ci in anon_items:
                ci.user_id = user_id
