package com.fabledsword.minstrel.models.wire

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

/**
 * Sync-specific shape for one artist row. Distinct from `ArtistWire`
 * (the regular API's display shape) — the sync endpoint returns the
 * raw DB row including `mbid` and the resolved cover paths, while the
 * regular API returns derived display fields (album_count, cover_url).
 */
@Serializable
data class SyncArtistWire(
    val id: String,
    val name: String = "",
    @SerialName("sort_name") val sortName: String = "",
    val mbid: String? = null,
    @SerialName("artist_thumb_path") val artistThumbPath: String? = null,
    @SerialName("artist_fanart_path") val artistFanartPath: String? = null,
)

@Serializable
data class SyncAlbumWire(
    val id: String,
    @SerialName("artist_id") val artistId: String,
    val title: String = "",
    @SerialName("sort_title") val sortTitle: String = "",
    @SerialName("release_date") val releaseDate: String? = null,
    @SerialName("cover_art_path") val coverArtPath: String? = null,
    val mbid: String? = null,
)

@Serializable
data class SyncTrackWire(
    val id: String,
    @SerialName("album_id") val albumId: String,
    @SerialName("artist_id") val artistId: String,
    val title: String = "",
    @SerialName("duration_ms") val durationMs: Int = 0,
    @SerialName("track_number") val trackNumber: Int? = null,
    @SerialName("disc_number") val discNumber: Int? = null,
    @SerialName("file_path") val filePath: String? = null,
    @SerialName("file_format") val fileFormat: String? = null,
    val genre: String? = null,
)

/**
 * `/api/library/sync` upsert bucket. Server batches all entity types
 * in one shot per cursor advance. v1 native client only consumes
 * artist/album/track — likes get their own `/api/likes/ids` refresh
 * path, and playlists pull on the Playlists screen visit.
 */
@Serializable
data class SyncUpsertsWire(
    val artist: List<SyncArtistWire> = emptyList(),
    val album: List<SyncAlbumWire> = emptyList(),
    val track: List<SyncTrackWire> = emptyList(),
)

/**
 * `/api/library/sync` delete bucket. Each list is a flat ID array;
 * the entity DAOs delete those rows.
 */
@Serializable
data class SyncDeletesWire(
    val artist: List<String> = emptyList(),
    val album: List<String> = emptyList(),
    val track: List<String> = emptyList(),
)

/**
 * Successful sync response body. `cursor` advances to the new
 * watermark; persist it back to sync_metadata so the next sync resumes
 * from there. The empty-delta case is signaled by HTTP 204 — the
 * SyncController handles that without parsing.
 *
 * 410 Gone means the cursor is too old to be useful; SyncController
 * destructively wipes the cache and retries from cursor=0.
 */
@Serializable
data class SyncResponseWire(
    val cursor: Long,
    val upserts: SyncUpsertsWire = SyncUpsertsWire(),
    val deletes: SyncDeletesWire = SyncDeletesWire(),
)
