use std::collections::{BTreeMap, BTreeSet};

pub use gotcha_gitea::{
    HistoryCommit, HomeData, IssueDetails, IssueEditorData, MilestoneDetails, Page, PullDetails,
    parse_api_date,
};
use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AppearanceMode {
    Light,
    Dark,
    #[default]
    #[serde(other)]
    Auto,
}

impl AppearanceMode {
    pub fn index(self) -> i32 {
        match self {
            Self::Auto => 0,
            Self::Light => 1,
            Self::Dark => 2,
        }
    }

    pub fn from_index(index: i32) -> Option<Self> {
        match index {
            0 => Some(Self::Auto),
            1 => Some(Self::Light),
            2 => Some(Self::Dark),
            _ => None,
        }
    }
}

#[derive(Clone, Deserialize, Serialize)]
pub struct Server {
    pub name: String,
    pub url: String,
    #[serde(skip)]
    pub token: String,
}

#[derive(Deserialize, Serialize)]
pub struct Preferences {
    #[serde(default)]
    pub servers: Vec<Server>,
    #[serde(default)]
    pub favorites: BTreeSet<String>,
    #[serde(default)]
    pub last_server: Option<usize>,
    #[serde(default = "open_status")]
    pub issue_status: String,
    #[serde(default)]
    pub issue_filters: BTreeMap<String, IssueFilter>,
    #[serde(default = "open_status")]
    pub pull_status: String,
    #[serde(default)]
    pub pull_filters: BTreeMap<String, PullFilter>,
    #[serde(default)]
    pub appearance: AppearanceMode,
}

impl Default for Preferences {
    fn default() -> Self {
        Self {
            servers: Vec::new(),
            favorites: BTreeSet::new(),
            last_server: None,
            issue_status: open_status(),
            issue_filters: BTreeMap::new(),
            pull_status: open_status(),
            pull_filters: BTreeMap::new(),
            appearance: AppearanceMode::default(),
        }
    }
}

#[derive(Clone, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct IssueFilter {
    #[serde(default)]
    pub milestone: String,
    #[serde(default)]
    pub labels: BTreeSet<String>,
}

impl IssueFilter {
    pub fn is_active(&self, status: &str) -> bool {
        status != "open" || self != &Self::default()
    }
}

#[derive(Clone, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct PullFilter {
    #[serde(default)]
    pub milestone: String,
}

impl PullFilter {
    pub fn is_active(&self, status: &str) -> bool {
        status != "open" || self != &Self::default()
    }
}

#[derive(Clone)]
pub struct RepositoryData {
    pub owner: String,
    pub name: String,
    pub description: String,
    pub language: String,
    pub open_issues: i64,
    pub updated: String,
    pub default_branch: String,
}

#[derive(Default)]
pub struct State {
    pub preferences: Preferences,
    pub active_server: Option<usize>,
    pub repositories: Vec<RepositoryData>,
}

pub struct IssueDraft {
    pub title: String,
    pub body: String,
    pub label_ids: Vec<i64>,
    pub milestone_id: Option<i64>,
    pub due_date: Option<i64>,
}

pub struct MilestoneDraft {
    pub title: String,
    pub description: String,
    pub due_date: Option<i64>,
}

pub fn open_status() -> String {
    "open".into()
}

pub fn civil_from_days(days: i64) -> (i64, i64, i64) {
    let days = days + 719_468;
    let era = days.div_euclid(146_097);
    let day_of_era = days - era * 146_097;
    let year_of_era =
        (day_of_era - day_of_era / 1_460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
    let mut year = year_of_era + era * 400;
    let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
    let month_prime = (5 * day_of_year + 2) / 153;
    let day = day_of_year - (153 * month_prime + 2) / 5 + 1;
    let month = month_prime + if month_prime < 10 { 3 } else { -9 };
    year += i64::from(month <= 2);
    (year, month, day)
}

pub fn days_from_civil(mut year: i64, month: i64, day: i64) -> i64 {
    year -= i64::from(month <= 2);
    let era = year.div_euclid(400);
    let year_of_era = year - era * 400;
    let month_prime = month + if month > 2 { -3 } else { 9 };
    let day_of_year = (153 * month_prime + 2) / 5 + day - 1;
    let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
    era * 146_097 + day_of_era - 719_468
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn issue_filter_default_state_is_owned_by_rust() {
        assert!(!IssueFilter::default().is_active("open"));
        assert!(IssueFilter::default().is_active("closed"));
        assert!(
            IssueFilter {
                milestone: "v1".into(),
                ..Default::default()
            }
            .is_active("open")
        );
        assert!(!PullFilter::default().is_active("open"));
        assert!(PullFilter::default().is_active("closed"));
        assert!(
            PullFilter {
                milestone: "v1".into(),
            }
            .is_active("open")
        );
    }
}
