use salvo::oapi::ToSchema;
use serde::{Deserialize, Serialize};

/// Query passed to subtitle providers.
#[derive(Debug, Clone)]
pub struct MediaQuery {
    pub title: String,
    pub season: Option<i32>,
    pub episode: Option<i32>,
    pub file_path: String,
}

/// A subtitle candidate returned by a provider.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SubtitlePayload {
    pub file_name: String,
    pub url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SubtitleCandidate {
    pub provider: String,
    pub language: String,
    pub is_bilingual: bool,
    pub payload: SubtitlePayload,
    pub score: i32,
}

impl SubtitleCandidate {
    #[must_use]
    pub fn new(
        provider: String,
        language: String,
        is_bilingual: bool,
        payload: SubtitlePayload,
        base_score: i32,
    ) -> Self {
        let score = base_score + Self::language_score(&language, is_bilingual);

        Self {
            provider,
            language,
            is_bilingual,
            payload,
            score,
        }
    }

    fn language_score(language: &str, is_bilingual: bool) -> i32 {
        let language = language.to_lowercase();

        if language.contains("chs") && language.contains("eng") || is_bilingual {
            100
        } else if language.contains("chs") || language.contains("简") {
            80
        } else if language.contains("cht") || language.contains("繁") {
            60
        } else {
            0
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{SubtitleCandidate, SubtitlePayload};

    #[test]
    fn candidate_score_includes_language_bonus() {
        let candidate = SubtitleCandidate::new(
            "subhd".to_string(),
            "简英".to_string(),
            true,
            SubtitlePayload {
                file_name: "test.ass".to_string(),
                url: "https://example.com/test.ass".to_string(),
            },
            7,
        );

        assert_eq!(candidate.score, 107);
    }
}

/// Format of a resolved subtitle file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum ResolvedSubtitleFormat {
    SubtitleFile,
    Zip,
    Rar,
    SevenZip,
    Unknown,
}

impl ResolvedSubtitleFormat {
    /// Infer format from file name extension.
    #[must_use]
    pub fn from_file_name(file_name: &str) -> Self {
        let lower = file_name.to_lowercase();
        match lower.split('.').next_back() {
            Some("srt") | Some("ass") | Some("ssa") | Some("sub") | Some("vtt") => {
                Self::SubtitleFile
            }
            Some("zip") => Self::Zip,
            Some("rar") => Self::Rar,
            Some("7z") => Self::SevenZip,
            _ => Self::Unknown,
        }
    }
}

/// Header metadata for a resolved subtitle.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ResolvedSubtitleHeader {
    pub file_name: String,
    pub format: ResolvedSubtitleFormat,
    pub language: String,
    pub is_bilingual: bool,
}

/// A resolved subtitle with payload and metadata.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ResolvedSubtitle {
    pub provider: String,
    pub header: ResolvedSubtitleHeader,
    pub download_url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct DownloadedSubtitle {
    pub header: ResolvedSubtitleHeader,
    #[serde(skip)]
    pub content: Vec<u8>,
}
