package com.ponzischeme89.memby.data.remote

import com.ponzischeme89.memby.data.model.BaseItem
import com.ponzischeme89.memby.data.model.GatewayFlagRequest
import com.ponzischeme89.memby.data.model.GatewayFeatures
import com.ponzischeme89.memby.data.model.GatewayDevices
import com.ponzischeme89.memby.data.model.GatewayDeviceNameRequest
import com.ponzischeme89.memby.data.model.GatewayHome
import com.ponzischeme89.memby.data.model.GatewayItems
import com.ponzischeme89.memby.data.model.GatewayLoginRequest
import com.ponzischeme89.memby.data.model.GatewayLoginResponse
import com.ponzischeme89.memby.data.model.GatewayMediaRequest
import com.ponzischeme89.memby.data.model.GatewayMediaRequestResult
import com.ponzischeme89.memby.data.model.GatewayMovieRatings
import com.ponzischeme89.memby.data.model.GatewayNextEpisode
import com.ponzischeme89.memby.data.model.GatewayPlayback
import com.ponzischeme89.memby.data.model.GatewayPlaybackReport
import com.ponzischeme89.memby.data.model.GatewayPrerollSchedule
import com.ponzischeme89.memby.data.model.GatewayRowEvents
import com.ponzischeme89.memby.data.model.GatewayJourneyEvents
import com.ponzischeme89.memby.data.model.GatewayRows
import com.ponzischeme89.memby.data.model.GatewaySearchHistory
import com.ponzischeme89.memby.data.model.GatewayRequestLookup
import com.ponzischeme89.memby.data.model.GatewayServiceStatus
import com.ponzischeme89.memby.data.model.GatewayUpdate
import com.ponzischeme89.memby.data.model.RecommendationOnboarding
import com.ponzischeme89.memby.data.model.RecommendationPreferences
import com.ponzischeme89.memby.data.model.UserItemData
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.DELETE
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query

/**
 * The Memby gateway API.
 *
 * Note how much smaller this is than [EmbyApi]: work the client used to do — fanning out
 * four home queries, picking an episode for a series, deciding which fields to request —
 * now happens server-side, which is the entire point of the gateway.
 */
interface GatewayApi {

    @POST("v1/auth/login")
    suspend fun login(@Body body: GatewayLoginRequest): GatewayLoginResponse

    @POST("v1/auth/logout")
    suspend fun logout()

    /** Confirms that a token restored from TV storage still exists on the gateway. */
    @GET("v1/auth/session")
    suspend fun session(): GatewayLoginResponse

    @GET("v1/auth/devices")
    suspend fun devices(): GatewayDevices

    @DELETE("v1/auth/devices/{deviceId}")
    suspend fun removeDevice(@Path("deviceId") deviceId: String)

    @PUT("v1/auth/devices/{deviceId}")
    suspend fun renameDevice(
        @Path("deviceId") deviceId: String,
        @Body body: GatewayDeviceNameRequest,
    )

    @GET("v1/home")
    suspend fun home(@Query("limit") limit: Int): GatewayHome

    @GET("v1/screensaver")
    suspend fun screensaver(@Query("limit") limit: Int): GatewayItems

    @GET("v1/search")
    suspend fun search(@Query("q") term: String, @Query("limit") limit: Int): GatewayItems

    /**
     * One page of a genre. A filter, not a query: the genre is the path rather than a term,
     * so the gateway can ask Emby the question actually being asked.
     */
    @GET("v1/genres/{genre}/items")
    suspend fun genreItems(
        @Path("genre") genre: String,
        @Query("offset") offset: Int,
        @Query("limit") limit: Int,
        @Query("type") itemType: String? = null,
    ): com.ponzischeme89.memby.data.model.GatewayGenrePage

    /** One paged media-type shelf without a genre filter. */
    @GET("v1/library/items")
    suspend fun libraryItems(
        @Query("offset") offset: Int,
        @Query("limit") limit: Int,
        @Query("type") itemType: String,
    ): com.ponzischeme89.memby.data.model.GatewayGenrePage

    /**
     * One month of the TV calendar. An absent [month] is the household's present month,
     * which is what a television asks for when the page opens.
     */
    @GET("v1/calendar")
    suspend fun calendar(
        @Query("month") month: String? = null,
    ): com.ponzischeme89.memby.data.model.GatewayCalendar

    @POST("v1/search/history")
    suspend fun recordSearch(@Body body: Map<String, String>)

    @GET("v1/search/history")
    suspend fun recentSearches(): GatewaySearchHistory

    @GET("v1/requests/lookup")
    suspend fun requestLookup(@Query("q") term: String): GatewayRequestLookup

    @POST("v1/requests")
    suspend fun requestMedia(@Body body: GatewayMediaRequest): GatewayMediaRequestResult

    /** Recommendation rows on their own. `/v1/home` already embeds these when warm. */
    @GET("v1/recommendations")
    suspend fun recommendations(): GatewayRows

    @GET("v1/for-you")
    suspend fun forYou(@Query("minutes") availableMinutes: Int): GatewayRows

    @GET("v1/recommendations/preferences")
    suspend fun recommendationPreferences(): RecommendationOnboarding

    @PUT("v1/recommendations/preferences")
    suspend fun saveRecommendationPreferences(@Body body: RecommendationPreferences)

    @GET("v1/preroll")
    suspend fun prerollSchedule(): GatewayPrerollSchedule

    /**
     * Whether this build should update. The version travels as a header on every request
     * (see GatewayServiceFactory), so there is nothing to pass here.
     */
    @GET("v1/update")
    suspend fun updateStatus(): GatewayUpdate

    @GET("v1/my-shows")
    suspend fun myShows(): com.ponzischeme89.memby.data.model.MyShowsResponse

    @POST("v1/my-shows")
    suspend fun saveMyShow(
        @Body body: com.ponzischeme89.memby.data.model.SaveMyShowRequest,
    ): com.ponzischeme89.memby.data.model.MyShowsResponse

    @DELETE("v1/my-shows/{id}")
    suspend fun removeMyShow(@Path("id") itemId: String)

    @GET("v1/notifications")
    suspend fun notifications(): com.ponzischeme89.memby.data.model.NotificationsResponse

    @PUT("v1/notifications")
    suspend fun setNotificationPreferences(
        @Body body: com.ponzischeme89.memby.data.model.NotificationPreferences,
    ): com.ponzischeme89.memby.data.model.NotificationsResponse

    @POST("v1/notifications/{id}/{action}")
    suspend fun updateNotification(
        @Path("id") id: Long,
        @Path("action") action: String,
    )

    /** Available during maintenance so an open app can be interrupted immediately. */
    @GET("v1/status")
    suspend fun serviceStatus(): GatewayServiceStatus

    /** Versioned server control-plane document; unknown flags remain safely ignorable. */
    @GET("v1/features")
    suspend fun features(): GatewayFeatures

    /**
     * The colour scheme in force and the ones this viewer may pick between. Fetched only
     * when the revision on the status poll moves — see `ThemeSync`.
     */
    @GET("v1/theme")
    suspend fun theme(): com.ponzischeme89.memby.data.model.GatewayThemeDocument

    /** This viewer's settings as the server holds them, for whichever TV they sit at. */
    @GET("v1/preferences")
    suspend fun preferences(): com.ponzischeme89.memby.data.model.GatewayPreferences

    /**
     * Writes them back. A 409 means somebody else — usually the operator — got there
     * first; the response body is their document, so the caller adopts rather than retries.
     */
    @PUT("v1/preferences")
    suspend fun savePreferences(
        @Body body: com.ponzischeme89.memby.data.model.GatewayPreferencesRequest,
    ): com.ponzischeme89.memby.data.model.GatewayPreferences

    @GET("v1/items/{id}")
    suspend fun item(@Path("id") itemId: String): BaseItem

    @GET("v1/people/{id}")
    suspend fun person(@Path("id") personId: String): BaseItem

    @GET("v1/people/{id}/filmography")
    suspend fun personFilmography(@Path("id") personId: String): GatewayItems

    /** Optional, server-filtered external movie ratings. Empty is always a valid result. */
    @GET("v1/items/{id}/ratings")
    suspend fun movieRatings(@Path("id") itemId: String): GatewayMovieRatings

    @GET("v1/items/{id}/season-finale")
    suspend fun seasonFinale(
        @Path("id") itemId: String,
    ): com.ponzischeme89.memby.data.model.GatewaySeasonFinale

    /** All episodes for a series in display order; the client groups them into seasons. */
    @GET("v1/items/{id}/episodes")
    suspend fun seriesEpisodes(@Path("id") seriesId: String): GatewayItems

    /** Why this viewer might enjoy the item, and what else in the library is like it. */
    @GET("v1/items/{id}/related")
    suspend fun related(
        @Path("id") itemId: String,
    ): com.ponzischeme89.memby.data.model.GatewayRelated

    @GET("v1/items/{id}/playback")
    suspend fun playback(
        @Path("id") itemId: String,
        @Query("type") itemType: String,
        @Query("title") title: String,
        @Query("resumePositionMs") resumePositionMs: Long,
        @Query("subtitleIndex") subtitleIndex: Int? = null,
        @Query("forceTranscode") forceTranscode: Boolean = false,
    ): GatewayPlayback

    /**
     * Where this episode's opening titles are, if Emby has marked them.
     *
     * Its own request for the same reason [trickplay] is: reading the markers costs the
     * gateway a round trip to Emby, and nothing about a skip button is needed before the
     * first frame — the earliest intro in a typical library starts a couple of minutes in.
     */
    @GET("v1/items/{id}/intro")
    suspend fun intro(
        @Path("id") itemId: String,
    ): com.ponzischeme89.memby.data.model.GatewayIntro

    /**
     * How this title's seek previews are laid out, if it has any.
     *
     * Deliberately its own request rather than a field on [playback]: reading the layout
     * costs the gateway a round trip to Emby, and the playback response is the one thing
     * standing between a Play press and a decoder starting. This is asked for once the
     * first frame is up.
     */
    @GET("v1/items/{id}/trickplay")
    suspend fun trickplay(
        @Path("id") itemId: String,
    ): com.ponzischeme89.memby.data.model.GatewayTrickplay

    /**
     * Ask the subtitle service for tracks this title does not have. This is a live query
     * against subtitle providers, so it is slow by nature — seconds, not milliseconds.
     * 404 when the gateway has no subtitle service or the operator has turned it off.
     */
    @GET("v1/items/{id}/subtitles/search")
    suspend fun searchSubtitles(
        @Path("id") itemId: String,
        @Query("language") language: String? = null,
    ): com.ponzischeme89.memby.data.model.GatewaySubtitleSearch

    /** Fetch one of those tracks. The gateway waits for Emby to notice the new file. */
    @POST("v1/items/{id}/subtitles/download")
    suspend fun downloadSubtitle(
        @Path("id") itemId: String,
        @Body body: com.ponzischeme89.memby.data.model.GatewaySubtitleDownloadRequest,
    ): com.ponzischeme89.memby.data.model.GatewaySubtitleDownload

    /** Check one existing subtitle against another and store a corrected copy when needed. */
    @POST("v1/items/{id}/subtitles/fix")
    suspend fun fixSubtitle(
        @Path("id") itemId: String,
        @Body body: com.ponzischeme89.memby.data.model.GatewaySubtitleFixRequest,
    ): com.ponzischeme89.memby.data.model.GatewaySubtitleFix

    /** 404 when nothing follows this item: a movie, or a series finale. */
    @GET("v1/items/{id}/next")
    suspend fun nextEpisode(
        @Path("id") itemId: String,
        @Query("seriesId") seriesId: String,
    ): GatewayNextEpisode

    @GET("v1/items/{id}/trailer")
    suspend fun trailer(@Path("id") itemId: String): BaseItem

    @POST("v1/items/{id}/favorite")
    suspend fun setFavorite(@Path("id") itemId: String, @Body body: GatewayFlagRequest): UserItemData

    @POST("v1/items/{id}/played")
    suspend fun setPlayed(@Path("id") itemId: String, @Body body: GatewayFlagRequest): UserItemData

    @POST("v1/items/{id}/hide-from-resume")
    suspend fun hideFromResume(@Path("id") itemId: String): UserItemData

    @POST("v1/playback/{phase}")
    suspend fun report(
        @Path("phase") phase: String,
        @Body body: GatewayPlaybackReport,
    ): com.ponzischeme89.memby.data.model.GatewayPlaybackReportResponse

    /** Row engagement, uploaded in batches. Fire-and-forget: failures are not retried. */
    @POST("v1/analytics/rows")
    suspend fun reportRowEvents(@Body body: GatewayRowEvents)

    /** Significant per-user journey steps. Fire-and-forget like row engagement. */
    @POST("v1/analytics/events")
    suspend fun reportJourneyEvents(@Body body: GatewayJourneyEvents)
}
