"""SQL implementation of the LikesService protocol.

Extracted from likes/bp/data/routes.py and likes/bp/actions/routes.py
to enable sx defquery/defaction conversion.
"""
from __future__ import annotations

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


def _Like():
    from models.like import Like
    return Like


class SqlLikesService:

    async def is_liked(
        self, session: AsyncSession, *,
        user_id: int, target_type: str,
        target_slug: str | None = None, target_id: int | None = None,
    ) -> bool:
        Like = _Like()
        if not user_id or not target_type:
            return False
        filters = [
            Like.user_id == user_id,
            Like.target_type == target_type,
            Like.deleted_at.is_(None),
        ]
        if target_slug is not None:
            filters.append(Like.target_slug == target_slug)
        elif target_id is not None:
            filters.append(Like.target_id == target_id)
        else:
            return False
        row = await session.scalar(select(Like.id).where(*filters))
        return row is not None

    async def liked_slugs(
        self, session: AsyncSession, *,
        user_id: int, target_type: str,
    ) -> list[str]:
        Like = _Like()
        if not user_id or not target_type:
            return []
        result = await session.execute(
            select(Like.target_slug).where(
                Like.user_id == user_id,
                Like.target_type == target_type,
                Like.target_slug.isnot(None),
                Like.deleted_at.is_(None),
            )
        )
        return list(result.scalars().all())

    async def liked_ids(
        self, session: AsyncSession, *,
        user_id: int, target_type: str,
    ) -> list[int]:
        Like = _Like()
        if not user_id or not target_type:
            return []
        result = await session.execute(
            select(Like.target_id).where(
                Like.user_id == user_id,
                Like.target_type == target_type,
                Like.target_id.isnot(None),
                Like.deleted_at.is_(None),
            )
        )
        return list(result.scalars().all())

    async def toggle(
        self, session: AsyncSession, *,
        user_id: int, target_type: str,
        target_slug: str | None = None, target_id: int | None = None,
    ) -> bool:
        """Toggle a like. Returns True if now liked, False if unliked."""
        Like = _Like()
        filters = [
            Like.user_id == user_id,
            Like.target_type == target_type,
            Like.deleted_at.is_(None),
        ]
        if target_slug is not None:
            filters.append(Like.target_slug == target_slug)
        elif target_id is not None:
            filters.append(Like.target_id == target_id)
        else:
            raise ValueError("target_slug or target_id required")

        existing = await session.scalar(select(Like).where(*filters))

        if existing:
            await session.execute(
                update(Like).where(Like.id == existing.id).values(deleted_at=func.now())
            )
            return False
        else:
            new_like = Like(
                user_id=user_id,
                target_type=target_type,
                target_slug=target_slug,
                target_id=target_id,
            )
            session.add(new_like)
            await session.flush()
            return True
