/**
 * Teapod
 *
 * Copyright 2020-2022  <seil0@mosad.xyz>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 */

package org.mosad.teapod.parser.crunchyroll

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.util.*

val supportedLocals = listOf(
    Locale.forLanguageTag("ar-SA"),
    Locale.forLanguageTag("de-DE"),
    Locale.forLanguageTag("en-US"),
    Locale.forLanguageTag("es-419"),
    Locale.forLanguageTag("es-ES"),
    Locale.forLanguageTag("fr-FR"),
    Locale.forLanguageTag("it-IT"),
    Locale.forLanguageTag("pt-BR"),
    Locale.forLanguageTag("pt-PT"),
    Locale.forLanguageTag("ru-RU"),
    Locale.ROOT
)

/**
 * data classes for browse
 * TODO make class names more clear/possibly overlapping for now
 */
enum class SortBy(val str: String) {
    ALPHABETICAL("alphabetical"),
    NEWLY_ADDED("newly_added"),
    POPULARITY("popularity")
}

@Suppress("unused")
enum class Categories(val str: String) {
    ACTION("action"),
    ADVENTURE("adventure"),
    COMEDY("comedy"),
    DRAMA("drama"),
    FANTASY("fantasy"),
    MUSIC("music"),
    ROMANCE("romance"),
    SCI_FI("sci-fi"),
    SEINEN("seinen"),
    SHOJO("shojo"),
    SHONEN("shonen"),
    SLICE_OF_LIFE("slice+of+life"),
    SPORTS("sports"),
    SUPERNATURAL("supernatural"),
    THRILLER("thriller")
}

/**
 * token, index, account. This must pe present for the app to work!
 */
@Serializable
data class Token(
    @SerialName("access_token") val accessToken: String,
    @SerialName("refresh_token") val refreshToken: String,
    @SerialName("expires_in") val expiresIn: Int,
    @SerialName("token_type") val tokenType: String,
    @SerialName("scope") val scope: String,
    @SerialName("country") val country: String,
    @SerialName("account_id") val accountId: String,
)

@Serializable
data class Index(
    @SerialName("cms") val cms: CMS,
    @SerialName("service_available") val serviceAvailable: Boolean,
)

@Serializable
data class CMS(
    @SerialName("bucket") val bucket: String,
    @SerialName("policy") val policy: String,
    @SerialName("signature") val signature: String,
    @SerialName("key_pair_id") val keyPairId: String,
    @SerialName("expires") val expires: String,
)

@Serializable
data class Account(
    @SerialName("account_id") val accountId: String,
    @SerialName("external_id") val externalId: String,
    @SerialName("email_verified") val emailVerified: Boolean,
    @SerialName("created") val created: String,
)
val NoneAccount = Account("", "", false, "")

/**
 * search, browse, DiscSeasonList, Watchlist, ContinueWatchingList data types all use Collection
 */

@Serializable
data class Collection<T>(
    @SerialName("total") val total: Int,
    @SerialName("items") val items: List<T>
)

@Serializable
data class Collection2<T>(
    @SerialName("total") val total: Int,
    @SerialName("data") val data: List<T>
)

typealias SearchResult = Collection<SearchCollection>
typealias SearchCollection = Collection<Item>
typealias BrowseResult = Collection<Item>
typealias SimilarToResult = Collection<Item>
typealias RecommendationsList = Collection<Item>
typealias Benefits = Collection<Benefit>

/**
 * panel data classes
 */

// the data class Item is used in browse, search, watchlist and similar to
// TODO rename to MediaPanel
@Serializable
data class Item(
    val id: String,
    val title: String,
    val type: String,
    val channel_id: String,
    val description: String,
    val images: Images
    // TODO series_metadata etc.
    // TODO add slug_title if present in search, browse, similar to
)

@Serializable
data class Images(val poster_tall: List<List<Poster>>, val poster_wide: List<List<Poster>>)
// crunchyroll why?

@Serializable
data class Poster(val height: Int, val width: Int, val source: String, val type: String)

/**
 * up next & watchlist data classes
 */

typealias Watchlist = Collection2<WatchlistItem>
typealias HistoryList = Collection2<UpNextAccountItem>
typealias UpNextSeriesList = Collection2<UpNextSeriesItem>

@Serializable
data class WatchlistItem(
    @SerialName("panel") val panel: EpisodePanel,
    @SerialName("new") val new: Boolean,
    @SerialName("playhead") val playhead: Int,
    @SerialName("fully_watched") val fullyWatched: Boolean = false,
    @SerialName("never_watched") val neverWatched: Boolean = false,
    @SerialName("is_favorite") val isFavorite: Boolean,
)

@Serializable
data class IsWatchlistItem(
    @SerialName("id") val id: String,
    @SerialName("is_favorite") val isFavorite: Boolean,
    @SerialName("date_added") val dateAdded: String
)

@Serializable
data class UpNextAccountItem(
    @SerialName("panel") val panel: EpisodePanel,
    @SerialName("new") val new: Boolean,
    @SerialName("playhead") val playhead: Int,
    @SerialName("fully_watched") val fullyWatched: Boolean = false,
)

@Serializable
data class UpNextSeriesItem(
    @SerialName("panel") val panel: EpisodePanel,
    @SerialName("playhead") val playhead: Int,
    @SerialName("fully_watched") val fullyWatched: Boolean,
    @SerialName("never_watched") val neverWatched: Boolean,

)

// EpisodePanel is used in ContinueWatchingItem and UpNextSeriesItem
@Serializable
data class EpisodePanel(
    @SerialName("id") val id: String,
    @SerialName("title") val title: String,
    @SerialName("type") val type: String,
    @SerialName("channel_id") val channelId: String,
    @SerialName("description") val description: String,
    @SerialName("episode_metadata") val episodeMetadata: EpisodeMetadata,
    @SerialName("images") val images: Thumbnail,
//    @SerialName("streams_link") val streamsLink: String,
)

@Serializable
data class EpisodeMetadata(
    @SerialName("duration_ms") val durationMs: Int,
    @SerialName("episode_number") val episodeNumber: Int? = null, // default/nullable value since optional
    @SerialName("season_id") val seasonId: String,
    @SerialName("season_number") val seasonNumber: Int,
    @SerialName("season_title") val seasonTitle: String,
    @SerialName("series_id") val seriesId: String,
    @SerialName("series_title") val seriesTitle: String,
)

val NoneCollection = Collection<Item>(0, emptyList())
val NoneSearchResult = SearchResult(0, emptyList())
val NoneBrowseResult = BrowseResult(0, emptyList())
val NoneSimilarToResult = SimilarToResult(0, emptyList())
val NoneWatchlist = Watchlist(0, emptyList())
val NoneHistoryList = HistoryList(0, emptyList())
val NoneUpNextSeriesList = UpNextSeriesList(0, emptyList())
val NoneRecommendationsList = RecommendationsList(0, emptyList())
val NoneBenefits = Benefits(0, emptyList())

/**
 * series data class
 */

typealias Series = Collection2<SeriesItem>

@Serializable
data class SeriesItem(
    @SerialName("id") val id: String,
    @SerialName("title") val title: String,
    @SerialName("description") val description: String,
    @SerialName("images") val images: Images,
    @SerialName("is_simulcast") val isSimulcast: Boolean,
    @SerialName("maturity_ratings") val maturityRatings: List<String>,
    @SerialName("audio_locales") val audioLocales: List<String>

)

val NoneSeriesItem = SeriesItem("", "", "", Images(emptyList(), emptyList()), false, emptyList(), emptyList())
val NoneSeries = Series(1, listOf(NoneSeriesItem))

/**
 * Seasons data classes
 */
@Serializable
data class Seasons(
    @SerialName("total") val total: Int,
    @SerialName("data") val data: List<Season>
)

@Serializable
data class Season(
    @SerialName("id") val id: String,
    @SerialName("title") val title: String,
    @SerialName("slug_title") val slugTitle: String,
    @SerialName("series_id") val seriesId: String,
    @SerialName("season_number") val seasonNumber: Int,
    @SerialName("is_subbed") val isSubbed: Boolean,
    @SerialName("is_dubbed") val isDubbed: Boolean,
)

val NoneSeasons = Seasons(0, emptyList())
val NoneSeason = Season("", "", "", "", 0, isSubbed = false, isDubbed = false)


/**
 * Episodes data classes
 */
@Serializable
data class Episodes(
    @SerialName("total") val total: Int,
    @SerialName("data") val data: List<Episode>
)

@Serializable
data class Episode(
    @SerialName("id") val id: String,
    @SerialName("title") val title: String,
    @SerialName("series_id") val seriesId: String,
    @SerialName("season_title") val seasonTitle: String,
    @SerialName("season_id") val seasonId: String,
    @SerialName("season_number") val seasonNumber: Int,
    @SerialName("episode") val episode: String,
    @SerialName("episode_number") val episodeNumber: Int? = null,
    @SerialName("description") val description: String,
    @SerialName("next_episode_id") val nextEpisodeId: String? = null, // default/nullable value since optional
    @SerialName("next_episode_title") val nextEpisodeTitle: String? = null, // default/nullable value since optional
    @SerialName("is_subbed") val isSubbed: Boolean,
    @SerialName("is_dubbed") val isDubbed: Boolean,
    @SerialName("images") val images: Thumbnail,
    @SerialName("duration_ms") val durationMs: Int,
    @SerialName("versions") val versions: List<Version>,
    @SerialName("streams_link") val streamsLink: String,
)

@Serializable
data class Thumbnail(
    @SerialName("thumbnail") val thumbnail: List<List<Poster>>
)

@Serializable
data class Version(
    @SerialName("audio_locale") val audioLocale: String,
    @SerialName("guid") val guid: String,
    @SerialName("is_premium_only") val isPremiumOnly: Boolean,
    @SerialName("media_guid") val mediaGUID: String,
    @SerialName("original") val original: Boolean,
    @SerialName("season_guid") val seasonGUID: String,
    @SerialName("variant") val variant: String,
)

val NoneEpisodes = Episodes(0, listOf())
val NoneEpisode = Episode(
    id = "",
    title = "",
    seriesId = "",
    seasonId = "",
    seasonTitle = "",
    seasonNumber = 0,
    episode = "",
    episodeNumber = 0,
    description = "",
    nextEpisodeId = "",
    nextEpisodeTitle = "",
    isSubbed = false,
    isDubbed = false,
    images = Thumbnail(listOf()),
    durationMs = 0,
    versions = emptyList(),
    streamsLink = ""
)

val NoneVersion = Version(
    audioLocale = "",
    guid = "",
    isPremiumOnly = false,
    mediaGUID = "",
    original = true,
    seasonGUID = "",
    variant = ""
)

typealias Playheads = Collection2<PlayheadObject>

@Serializable
data class PlayheadObject(
    @SerialName("playhead") val playhead: Int,
    @SerialName("content_id") val contentId: String,
    @SerialName("fully_watched") val fullyWatched: Boolean,
    @SerialName("last_modified") val lastModified: String,
)

val NonePlayheads = Playheads(0, emptyList())

/**
 * Meta data for a episode intro. All time values are in seconds.
 */
@Serializable
data class DatalabIntro(
    @SerialName("media_id") val mediaId: String,
    @SerialName("startTime") val startTime: Float,
    @SerialName("endTime") val endTime: Float,
    @SerialName("duration") val duration: Float,
    @SerialName("comparedWith") val comparedWith: String,
    @SerialName("ordering") val ordering: String,
    @SerialName("last_updated") val lastUpdated: String,
)

val NoneDatalabIntro = DatalabIntro("", 0f, 0f, 0f, "", "", "")

/**
 * playback/stream data classes
 */
@Serializable
data class Streams(
    @SerialName("total") val total: Int,
    @SerialName("data") val data: List<StreamList>,
)

@Serializable
data class StreamList(
    @SerialName("adaptive_dash") val adaptive_dash: Map<String, Stream>,
    @SerialName("adaptive_hls") val adaptive_hls: Map<String, Stream>,
    @SerialName("download_dash") val downloadDash: Map<String, Stream>,
    @SerialName("download_hls") val download_hls: Map<String, Stream>,
//    @SerialName("drm_adaptive_dash") val drmAdaptiveDash: Map<String, Stream>,
//    @SerialName("drm_adaptive_hls") val drmAdaptiveHls: Map<String, Stream>,
//    @SerialName("drm_download_dash") val drmDownloadDash: Map<String, Stream>,
//    @SerialName("drm_download_hls") val drmDownloadHls: Map<String, Stream>,
//    @SerialName("vo_adaptive_dash") val vo_adaptive_dash: Map<String, Stream>,
//    @SerialName("vo_adaptive_hls") val vo_adaptive_hls: Map<String, Stream>,
//    @SerialName("vo_drm_adaptive_dash") val vo_drm_adaptive_dash: Map<String, Stream>,
//    @SerialName("vo_drm_adaptive_hls") val vo_drm_adaptive_hls: Map<String, Stream>,
)

@Serializable
data class Stream(
    @SerialName("hardsub_locale") val hardsubLocale: String = "", // default/nullable value since might be optional
    @SerialName("url") val url: String = "", // default/nullable value since optional
    @SerialName("vcodec") val vcodec: String = "", // default/nullable value since optional
)

val NoneStreams = Streams(
    0,
    arrayListOf(StreamList(
        mapOf(), mapOf(), mapOf(), mapOf()
    ))
)

/**
 * profile data class
 */
@Serializable
data class Profile(
    @SerialName("avatar") val avatar: String,
    @SerialName("email") val email: String,
    @SerialName("maturity_rating") val maturityRating: String,
    @SerialName("preferred_content_audio_language") val preferredContentAudioLanguage: String,
    @SerialName("preferred_content_subtitle_language") val preferredContentSubtitleLanguage: String,
    @SerialName("username") val username: String,
)
val NoneProfile = Profile(
    avatar = "",
    email = "",
    maturityRating = "",
    preferredContentAudioLanguage = "",
    preferredContentSubtitleLanguage = "",
    username = ""
)

/**
 * benefit data class
 */
@Serializable
data class Benefit(
    @SerialName("benefit") val benefit: String,
    @SerialName("source") val source: String,
)
val NoneBenefit = Benefit(
    benefit = "",
    source = ""
)
