use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GameSource {
    Steam,
    Epic,
    Gog,
    Local,
    Unknown,
}

impl GameSource {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Steam => "steam",
            Self::Epic => "epic",
            Self::Gog => "gog",
            Self::Local => "local",
            Self::Unknown => "unknown",
        }
    }

    pub fn from_str(value: &str) -> Self {
        match value {
            "steam" => Self::Steam,
            "epic" => Self::Epic,
            "gog" => Self::Gog,
            "local" => Self::Local,
            _ => Self::Unknown,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MetadataStatus {
    Pending,
    Matched,
    NeedsReview,
    Manual,
}

impl MetadataStatus {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Pending => "pending",
            Self::Matched => "matched",
            Self::NeedsReview => "needs_review",
            Self::Manual => "manual",
        }
    }

    pub fn from_str(value: &str) -> Self {
        match value {
            "matched" => Self::Matched,
            "needs_review" => Self::NeedsReview,
            "manual" => Self::Manual,
            _ => Self::Pending,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LibraryItemKind {
    Game,
    Tool,
    Software,
    Unknown,
}

impl LibraryItemKind {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Game => "game",
            Self::Tool => "tool",
            Self::Software => "software",
            Self::Unknown => "unknown",
        }
    }

    pub fn from_str(value: &str) -> Self {
        match value {
            "tool" => Self::Tool,
            "software" => Self::Software,
            "unknown" => Self::Unknown,
            _ => Self::Game,
        }
    }
}

pub fn infer_library_item_kind(
    steam_app_type: Option<&str>,
    title: Option<&str>,
    genres: &[String],
    categories: &[String],
) -> LibraryItemKind {
    if steam_app_type
        .map(|value| value.eq_ignore_ascii_case("software"))
        .unwrap_or(false)
    {
        return LibraryItemKind::Software;
    }

    if genres.iter().any(|genre| is_tool_label(genre))
        || categories.iter().any(|category| is_tool_label(category))
        || title.map(is_tool_title).unwrap_or(false)
    {
        return LibraryItemKind::Tool;
    }

    if steam_app_type
        .map(|value| value.eq_ignore_ascii_case("game"))
        .unwrap_or(false)
    {
        LibraryItemKind::Game
    } else {
        LibraryItemKind::Unknown
    }
}

fn is_tool_label(value: &str) -> bool {
    matches!(
        normalize_label(value).as_str(),
        "animation & modeling"
            | "audio production"
            | "design & illustration"
            | "education"
            | "game development"
            | "photo editing"
            | "software training"
            | "utilities"
            | "video production"
            | "web publishing"
            | "includes level editor"
    )
}

fn is_tool_title(value: &str) -> bool {
    let value = normalize_label(value);
    value == "blender"
        || value.contains("creation kit")
        || value.contains("mod tools")
        || value.contains("modding tools")
        || value.contains("developer tools")
        || value.contains("dedicated server")
        || value.ends_with(" sdk")
        || value.contains(" editor")
}

fn normalize_label(value: &str) -> String {
    value.trim().to_ascii_lowercase()
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GameCandidate {
    pub title: String,
    pub source: GameSource,
    pub app_id: Option<String>,
    pub install_path: PathBuf,
    pub executable_path: Option<PathBuf>,
    pub launch_command: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GameMetadata {
    pub title: Option<String>,
    pub description: Option<String>,
    pub app_kind: LibraryItemKind,
    pub steam_app_type: Option<String>,
    pub genres: Vec<String>,
    pub steam_categories: Vec<String>,
    pub release_date: Option<String>,
    pub developer: Option<String>,
    pub publisher: Option<String>,
    pub cover_image: Option<PathBuf>,
    pub hero_image: Option<PathBuf>,
    pub icon_image: Option<PathBuf>,
    pub status: MetadataStatus,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LibraryGame {
    pub id: i64,
    pub title: String,
    pub platform_source: GameSource,
    pub source_store: String,
    pub source_app_id: Option<String>,
    pub install_path: String,
    pub executable_path: Option<String>,
    pub launch_command: Option<String>,
    pub description: Option<String>,
    pub app_kind: LibraryItemKind,
    pub steam_app_type: Option<String>,
    pub genres: Vec<String>,
    pub steam_categories: Vec<String>,
    pub release_date: Option<String>,
    pub developer: Option<String>,
    pub publisher: Option<String>,
    pub cover_image: Option<String>,
    pub hero_image: Option<String>,
    pub icon_image: Option<String>,
    pub metadata_status: MetadataStatus,
    pub last_scanned: i64,
    pub user_hidden: bool,
    pub user_favourite: bool,
    pub user_title: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ScanProviderReport {
    pub source: GameSource,
    pub discovered: usize,
    pub error: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ScanSummary {
    pub discovered: usize,
    pub inserted_or_updated: usize,
    pub metadata_matched: usize,
    pub unmatched: usize,
    pub providers: Vec<ScanProviderReport>,
    pub games: Vec<LibraryGame>,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LibraryScanRequest {
    #[serde(default)]
    pub local_folders: Vec<String>,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GameCustomizationRequest {
    pub game_id: i64,
    pub title: Option<String>,
    pub executable_path: Option<String>,
    pub hidden: Option<bool>,
    pub favourite: Option<bool>,
}
