"""Protocol classes defining each domain's service interface.

All cross-domain callers program against these Protocols.  Concrete
implementations (Sql*Service) and no-op stubs both satisfy them.
"""
from __future__ import annotations

from datetime import datetime
from typing import Protocol, runtime_checkable

from sqlalchemy.ext.asyncio import AsyncSession

from .dtos import (
    PostDTO,
    CalendarDTO,
    CalendarEntryDTO,
    TicketDTO,
    MarketPlaceDTO,
    ProductDTO,
    CartItemDTO,
    CartSummaryDTO,
    ActorProfileDTO,
    APActivityDTO,
    APFollowerDTO,
)


@runtime_checkable
class BlogService(Protocol):
    async def get_post_by_slug(self, session: AsyncSession, slug: str) -> PostDTO | None: ...
    async def get_post_by_id(self, session: AsyncSession, id: int) -> PostDTO | None: ...
    async def get_posts_by_ids(self, session: AsyncSession, ids: list[int]) -> list[PostDTO]: ...

    async def search_posts(
        self, session: AsyncSession, query: str, page: int = 1, per_page: int = 10,
    ) -> tuple[list[PostDTO], int]: ...


@runtime_checkable
class CalendarService(Protocol):
    async def calendars_for_container(
        self, session: AsyncSession, container_type: str, container_id: int,
    ) -> list[CalendarDTO]: ...

    async def pending_entries(
        self, session: AsyncSession, *, user_id: int | None, session_id: str | None,
    ) -> list[CalendarEntryDTO]: ...

    async def entries_for_page(
        self, session: AsyncSession, page_id: int, *, user_id: int | None, session_id: str | None,
    ) -> list[CalendarEntryDTO]: ...

    async def entry_by_id(self, session: AsyncSession, entry_id: int) -> CalendarEntryDTO | None: ...

    async def associated_entries(
        self, session: AsyncSession, content_type: str, content_id: int, page: int,
    ) -> tuple[list[CalendarEntryDTO], bool]: ...

    async def toggle_entry_post(
        self, session: AsyncSession, entry_id: int, content_type: str, content_id: int,
    ) -> bool: ...

    async def adopt_entries_for_user(
        self, session: AsyncSession, user_id: int, session_id: str,
    ) -> None: ...

    async def claim_entries_for_order(
        self, session: AsyncSession, order_id: int, user_id: int | None,
        session_id: str | None, page_post_id: int | None,
    ) -> None: ...

    async def confirm_entries_for_order(
        self, session: AsyncSession, order_id: int, user_id: int | None,
        session_id: str | None,
    ) -> None: ...

    async def get_entries_for_order(
        self, session: AsyncSession, order_id: int,
    ) -> list[CalendarEntryDTO]: ...

    async def user_tickets(
        self, session: AsyncSession, *, user_id: int,
    ) -> list[TicketDTO]: ...

    async def user_bookings(
        self, session: AsyncSession, *, user_id: int,
    ) -> list[CalendarEntryDTO]: ...

    async def confirmed_entries_for_posts(
        self, session: AsyncSession, post_ids: list[int],
    ) -> dict[int, list[CalendarEntryDTO]]: ...

    async def pending_tickets(
        self, session: AsyncSession, *, user_id: int | None, session_id: str | None,
    ) -> list[TicketDTO]: ...

    async def tickets_for_page(
        self, session: AsyncSession, page_id: int, *, user_id: int | None, session_id: str | None,
    ) -> list[TicketDTO]: ...

    async def claim_tickets_for_order(
        self, session: AsyncSession, order_id: int, user_id: int | None,
        session_id: str | None, page_post_id: int | None,
    ) -> None: ...

    async def confirm_tickets_for_order(
        self, session: AsyncSession, order_id: int,
    ) -> None: ...

    async def get_tickets_for_order(
        self, session: AsyncSession, order_id: int,
    ) -> list[TicketDTO]: ...

    async def adopt_tickets_for_user(
        self, session: AsyncSession, user_id: int, session_id: str,
    ) -> None: ...

    async def adjust_ticket_quantity(
        self, session: AsyncSession, entry_id: int, count: int, *,
        user_id: int | None, session_id: str | None,
        ticket_type_id: int | None = None,
    ) -> int: ...

    async def entry_ids_for_content(
        self, session: AsyncSession, content_type: str, content_id: int,
    ) -> set[int]: ...

    async def visible_entries_for_period(
        self, session: AsyncSession, calendar_id: int,
        period_start: datetime, period_end: datetime,
        *, user_id: int | None, is_admin: bool, session_id: str | None,
    ) -> list[CalendarEntryDTO]: ...


@runtime_checkable
class MarketService(Protocol):
    async def marketplaces_for_container(
        self, session: AsyncSession, container_type: str, container_id: int,
    ) -> list[MarketPlaceDTO]: ...

    async def product_by_id(self, session: AsyncSession, product_id: int) -> ProductDTO | None: ...

    async def create_marketplace(
        self, session: AsyncSession, container_type: str, container_id: int,
        name: str, slug: str,
    ) -> MarketPlaceDTO: ...

    async def soft_delete_marketplace(
        self, session: AsyncSession, container_type: str, container_id: int,
        slug: str,
    ) -> bool: ...


@runtime_checkable
class CartService(Protocol):
    async def cart_summary(
        self, session: AsyncSession, *, user_id: int | None, session_id: str | None,
        page_slug: str | None = None,
    ) -> CartSummaryDTO: ...

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

    async def adopt_cart_for_user(
        self, session: AsyncSession, user_id: int, session_id: str,
    ) -> None: ...


@runtime_checkable
class FederationService(Protocol):
    # -- Actor management -----------------------------------------------------
    async def get_actor_by_username(
        self, session: AsyncSession, username: str,
    ) -> ActorProfileDTO | None: ...

    async def get_actor_by_user_id(
        self, session: AsyncSession, user_id: int,
    ) -> ActorProfileDTO | None: ...

    async def create_actor(
        self, session: AsyncSession, user_id: int, preferred_username: str,
        display_name: str | None = None, summary: str | None = None,
    ) -> ActorProfileDTO: ...

    async def username_available(
        self, session: AsyncSession, username: str,
    ) -> bool: ...

    # -- Publishing (core cross-domain API) -----------------------------------
    async def publish_activity(
        self, session: AsyncSession, *,
        actor_user_id: int,
        activity_type: str,
        object_type: str,
        object_data: dict,
        source_type: str | None = None,
        source_id: int | None = None,
    ) -> APActivityDTO: ...

    # -- Queries --------------------------------------------------------------
    async def get_activity(
        self, session: AsyncSession, activity_id: str,
    ) -> APActivityDTO | None: ...

    async def get_outbox(
        self, session: AsyncSession, username: str,
        page: int = 1, per_page: int = 20,
    ) -> tuple[list[APActivityDTO], int]: ...

    async def get_activity_for_source(
        self, session: AsyncSession, source_type: str, source_id: int,
    ) -> APActivityDTO | None: ...

    # -- Followers ------------------------------------------------------------
    async def get_followers(
        self, session: AsyncSession, username: str,
    ) -> list[APFollowerDTO]: ...

    async def add_follower(
        self, session: AsyncSession, username: str,
        follower_acct: str, follower_inbox: str, follower_actor_url: str,
        follower_public_key: str | None = None,
    ) -> APFollowerDTO: ...

    async def remove_follower(
        self, session: AsyncSession, username: str, follower_acct: str,
    ) -> bool: ...

    # -- Stats ----------------------------------------------------------------
    async def get_stats(self, session: AsyncSession) -> dict: ...
