package com.fabledsword.minstrel.models.wire

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
 * One row of `GET /api/lidarr/search`. Mirrors
 * `web/src/lib/api/types.ts LidarrSearchResult` /
 * `flutter_client/lib/models/lidarr.dart LidarrSearchResult`.
 *
 * `inLibrary` and `requested` let the UI greyout rows the user can't
 * act on (already imported / already awaiting review). All defaults
 * empty so missing keys don't throw on decode.
 */
@Serializable
data class LidarrSearchResultWire(
    val mbid: String = "",
    val name: String = "",
    @SerialName("secondary_text") val secondaryText: String = "",
    @SerialName("image_url") val imageUrl: String = "",
    @SerialName("artist_mbid") val artistMbid: String = "",
    @SerialName("album_mbid") val albumMbid: String = "",
    @SerialName("in_library") val inLibrary: Boolean = false,
    val requested: Boolean = false,
)

/**
 * `GET /api/discover/suggestions` row — an out-of-library artist
 * surfaced from ListenBrainz-derived seeds. `imageUrl` is resolved
 * on-demand from Lidarr server-side and may be empty.
 */
@Serializable
data class ArtistSuggestionWire(
    val mbid: String = "",
    val name: String = "",
    @SerialName("image_url") val imageUrl: String = "",
    val attribution: List<SeedContributionWire> = emptyList(),
    /**
     * Tags this candidate shares with the user's taste profile, strongest
     * first (max 3, #2377). Absent for most candidates — tag coverage for
     * out-of-library artists is partial by nature (#2376) — so the default
     * empty list is the common case, not an error.
     */
    @SerialName("matched_tags") val matchedTags: List<String> = emptyList(),
)

/**
 * Per-seed attribution for an ArtistSuggestion — "because you liked X"
 * / "because you played Y". `isLiked = true` means the seed came from
 * the user's likes; false means a played-but-not-liked play.
 */
@Serializable
data class SeedContributionWire(
    val name: String = "",
    @SerialName("is_liked") val isLiked: Boolean = false,
)

/**
 * One row of `GET /api/discover/snoozes` — a suggestion the user parked
 * with "not right now". The server only returns rows that are still in
 * effect, so the client never compares [snoozedUntil] against the clock to
 * decide whether to show it; it reads it only to say when the artist comes
 * back.
 */
@Serializable
data class SuggestionSnoozeWire(
    val mbid: String = "",
    val name: String = "",
    @SerialName("snoozed_until") val snoozedUntil: String = "",
    @SerialName("created_at") val createdAt: String = "",
)

/**
 * Body for `POST /api/discover/suggestions/{mbid}/snooze`.
 *
 * [name] is required by the server, not decorative: suggestions are
 * out-of-library, so there is no artists row to resolve a display name from
 * and the snooze list would have nothing to render. Omitting it is a 400.
 *
 * No `days` field. The duration is the server's to own (90 days); pinning it
 * client-side would freeze the default at whatever this build shipped.
 */
@Serializable
data class SnoozeSuggestionBody(
    val name: String,
)

/**
 * Body posted to `POST /api/requests`. Mirrors the Flutter `createRequest`
 * payload shape. Optional fields are emitted only when non-null
 * server-side; matching that here keeps the wire identical.
 */
@Serializable
data class CreateRequestBody(
    val kind: String,
    @SerialName("lidarr_artist_mbid") val artistMbid: String,
    @SerialName("artist_name") val artistName: String,
    @SerialName("lidarr_album_mbid") val albumMbid: String? = null,
    @SerialName("album_title") val albumTitle: String? = null,
    @SerialName("lidarr_track_mbid") val trackMbid: String? = null,
    @SerialName("track_title") val trackTitle: String? = null,
)
