"""Subsonic search endpoints — search3 over the library repositories.

Mirrors the native ``/api/v1/search/library`` fan-out (tracks/albums/artists),
reshaped into the Subsonic ``searchResult3`` element. An empty query returns
results so clients can use search3 to browse.
"""

from typing import Annotated, Any

from fastapi import APIRouter, Query, Response

from app.api.deps import AlbumRepoDep, ArtistRepoDep, SubsonicFormat, SubsonicUser, TrackRepoDep
from app.api.rest.envelope import subsonic_response
from app.api.rest.serializers import album_dict, artist_dict, song_dict

router = APIRouter()


@router.api_route("/search3", methods=["GET", "POST"])
@router.api_route("/search3.view", methods=["GET", "POST"])
async def search3(
    _user: SubsonicUser,
    fmt: SubsonicFormat,
    track_repo: TrackRepoDep,
    artist_repo: ArtistRepoDep,
    album_repo: AlbumRepoDep,
    query: Annotated[str, Query()] = "",
    artistCount: Annotated[int, Query(ge=0, le=500)] = 20,
    artistOffset: Annotated[int, Query(ge=0)] = 0,
    albumCount: Annotated[int, Query(ge=0, le=500)] = 20,
    albumOffset: Annotated[int, Query(ge=0)] = 0,
    songCount: Annotated[int, Query(ge=0, le=500)] = 20,
    songOffset: Annotated[int, Query(ge=0)] = 0,
) -> Response:
    # Subsonic sends "" (and some clients '""') to mean "everything".
    q: str | None = query.strip().strip('"') or None

    artists_out: list[dict[str, Any]] = []
    if artistCount:
        artists = await artist_repo.list(q=q, limit=artistCount, offset=artistOffset)
        for a in artists:
            album_count = await artist_repo.album_count(a.id)
            artists_out.append(artist_dict(a, album_count=album_count))

    albums_out: list[dict[str, Any]] = []
    if albumCount:
        albums = await album_repo.list(artist_id=None, q=q, limit=albumCount, offset=albumOffset)
        album_artist_ids = list({a.artist_id for a in albums})
        album_artist_map = {a.id: a for a in await artist_repo.get_many(album_artist_ids)}
        counts = await album_repo.track_count_many([a.id for a in albums])
        albums_out = [
            album_dict(a, album_artist_map.get(a.artist_id), song_count=counts.get(a.id, 0))
            for a in albums
        ]

    songs_out: list[dict[str, Any]] = []
    if songCount:
        tracks = await track_repo.list(
            artist_id=None,
            album_id=None,
            q=q,
            sort_by="title",
            order="asc",
            limit=songCount,
            offset=songOffset,
        )
        song_artist_map = {
            a.id: a for a in await artist_repo.get_many(list({t.artist_id for t in tracks}))
        }
        song_album_map = {
            a.id: a
            for a in await album_repo.get_many(
                list({t.album_id for t in tracks if t.album_id is not None})
            )
        }
        songs_out = [
            song_dict(
                t,
                song_artist_map.get(t.artist_id),
                song_album_map.get(t.album_id) if t.album_id is not None else None,
            )
            for t in tracks
        ]

    payload = {"searchResult3": {"artist": artists_out, "album": albums_out, "song": songs_out}}
    return subsonic_response(payload, fmt=fmt)
