"""Like repository — adapter over ``AsyncSession``.

Likes are an append-only event log. Current state = latest event per (user, track).
"""

import uuid

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

from app.domain.entities.like import Like
from app.domain.entities.track import Track
from app.infrastructure.db.models.like import LikeModel
from app.infrastructure.db.models.track import TrackModel


def _to_entity(row: LikeModel) -> Like:
    return Like(
        id=row.id,
        user_id=row.user_id,
        track_id=row.track_id,
        value=row.value,
        created_at=row.created_at,
    )


def _track_to_entity(row: TrackModel) -> Track:
    return Track(
        id=row.id,
        title=row.title,
        artist_id=row.artist_id,
        album_id=row.album_id,
        storage_uri=row.storage_uri,
        file_format=row.file_format,
        file_size=row.file_size,
        source=row.source,
        source_id=row.source_id,
        duration_seconds=row.duration_seconds,
        genre=row.genre,
        year=row.year,
        track_number=row.track_number,
        metadata_status=row.metadata_status,
        metadata_error=row.metadata_error,
        enriched_at=row.enriched_at,
        created_at=row.created_at,
        updated_at=row.updated_at,
    )


class SqlAlchemyLikeRepository:
    def __init__(self, session: AsyncSession) -> None:
        self._session = session

    async def add(self, *, user_id: uuid.UUID, track_id: uuid.UUID, value: str) -> Like:
        row = LikeModel(user_id=user_id, track_id=track_id, value=value)
        self._session.add(row)
        await self._session.flush()
        await self._session.refresh(row)
        return _to_entity(row)

    async def get_latest_state(
        self, *, user_id: uuid.UUID, track_ids: list[uuid.UUID]
    ) -> list[Like]:
        if not track_ids:
            return []
        # Subquery: max(created_at) per track for this user
        max_sq = (
            select(
                LikeModel.track_id,
                func.max(LikeModel.created_at).label("latest"),
            )
            .where(LikeModel.user_id == user_id, LikeModel.track_id.in_(track_ids))
            .group_by(LikeModel.track_id)
            .subquery()
        )
        rows = (
            (
                await self._session.execute(
                    select(LikeModel)
                    .join(
                        max_sq,
                        (LikeModel.track_id == max_sq.c.track_id)
                        & (LikeModel.created_at == max_sq.c.latest),
                    )
                    .where(LikeModel.user_id == user_id)
                )
            )
            .scalars()
            .all()
        )
        return [_to_entity(r) for r in rows]

    async def list_liked_tracks(
        self, *, user_id: uuid.UUID, limit: int, offset: int
    ) -> list[Track]:
        # Tracks where the latest like event has value='like', ordered by like time desc
        max_sq = (
            select(
                LikeModel.track_id,
                func.max(LikeModel.created_at).label("latest"),
            )
            .where(LikeModel.user_id == user_id)
            .group_by(LikeModel.track_id)
            .subquery()
        )
        liked_sq = (
            select(LikeModel.track_id, LikeModel.created_at)
            .join(
                max_sq,
                (LikeModel.track_id == max_sq.c.track_id)
                & (LikeModel.created_at == max_sq.c.latest),
            )
            .where(LikeModel.user_id == user_id, LikeModel.value == "like")
            .subquery()
        )
        rows = (
            (
                await self._session.execute(
                    select(TrackModel)
                    .join(liked_sq, TrackModel.id == liked_sq.c.track_id)
                    .order_by(liked_sq.c.created_at.desc())
                    .limit(limit)
                    .offset(offset)
                )
            )
            .scalars()
            .all()
        )
        return [_track_to_entity(r) for r in rows]

    async def count_liked_tracks(self, *, user_id: uuid.UUID) -> int:
        max_sq = (
            select(
                LikeModel.track_id,
                func.max(LikeModel.created_at).label("latest"),
            )
            .where(LikeModel.user_id == user_id)
            .group_by(LikeModel.track_id)
            .subquery()
        )
        liked_sq = (
            select(LikeModel.track_id)
            .join(
                max_sq,
                (LikeModel.track_id == max_sq.c.track_id)
                & (LikeModel.created_at == max_sq.c.latest),
            )
            .where(LikeModel.user_id == user_id, LikeModel.value == "like")
            .subquery()
        )
        return (
            await self._session.execute(select(func.count()).select_from(liked_sq))
        ).scalar_one()
