from __future__ import annotations

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.sql import func

from models.calendars import CalendarEntry, CalendarEntryPost
from shared.infrastructure.data_client import fetch_data
from shared.contracts.dtos import PostDTO, dto_from_dict


async def add_post_to_entry(
    session: AsyncSession,
    entry_id: int,
    post_id: int
) -> tuple[bool, str | None]:
    """
    Associate a post with a calendar entry.
    Returns (success, error_message).
    """
    # Check if entry exists
    entry = await session.scalar(
        select(CalendarEntry).where(
            CalendarEntry.id == entry_id,
            CalendarEntry.deleted_at.is_(None)
        )
    )
    if not entry:
        return False, "Calendar entry not found"

    # Check if post exists
    raw = await fetch_data("blog", "post-by-id", params={"id": post_id}, required=False)
    if not raw:
        return False, "Post not found"

    # Check if association already exists
    existing = await session.scalar(
        select(CalendarEntryPost).where(
            CalendarEntryPost.entry_id == entry_id,
            CalendarEntryPost.content_type == "post",
            CalendarEntryPost.content_id == post_id,
            CalendarEntryPost.deleted_at.is_(None)
        )
    )

    if existing:
        return False, "Post is already associated with this entry"

    # Create association
    association = CalendarEntryPost(
        entry_id=entry_id,
        content_type="post",
        content_id=post_id
    )
    session.add(association)
    await session.flush()

    return True, None


async def remove_post_from_entry(
    session: AsyncSession,
    entry_id: int,
    post_id: int
) -> tuple[bool, str | None]:
    """
    Remove a post association from a calendar entry (soft delete).
    Returns (success, error_message).
    """
    # Find the association
    association = await session.scalar(
        select(CalendarEntryPost).where(
            CalendarEntryPost.entry_id == entry_id,
            CalendarEntryPost.content_type == "post",
            CalendarEntryPost.content_id == post_id,
            CalendarEntryPost.deleted_at.is_(None)
        )
    )

    if not association:
        return False, "Association not found"

    # Soft delete
    association.deleted_at = func.now()
    await session.flush()

    return True, None


async def get_entry_posts(
    session: AsyncSession,
    entry_id: int
) -> list:
    """
    Get all posts (as PostDTOs) associated with a calendar entry.
    """
    result = await session.execute(
        select(CalendarEntryPost.content_id).where(
            CalendarEntryPost.entry_id == entry_id,
            CalendarEntryPost.content_type == "post",
            CalendarEntryPost.deleted_at.is_(None),
        )
    )
    post_ids = list(result.scalars().all())
    if not post_ids:
        return []
    raw_posts = await fetch_data("blog", "posts-by-ids",
                                 params={"ids": ",".join(str(i) for i in post_ids)},
                                 required=False) or []
    posts = [dto_from_dict(PostDTO, p) for p in raw_posts]
    return sorted(posts, key=lambda p: (p.title or ""))


async def search_posts(
    session: AsyncSession,
    query: str,
    page: int = 1,
    per_page: int = 10
) -> tuple[list, int]:
    """
    Search for posts by title with pagination.
    If query is empty, returns all posts in published order.
    Returns (post_dtos, total_count).
    """
    raw = await fetch_data("blog", "search-posts",
                           params={"query": query, "page": page, "per_page": per_page},
                           required=False) or {"posts": [], "total": 0}
    posts = [dto_from_dict(PostDTO, p) for p in raw.get("posts", [])]
    return posts, raw.get("total", 0)
