use std::{collections::HashMap, fmt::Display};

use crate::status::count_statuses;

use super::{jobs::Job, status::Status};

#[derive(Debug, Default)]
pub(crate) struct RunnerState {
    pub(super) jobs: HashMap<String, Job>, // map index to job
    pub(super) statuses: HashMap<String, Status>, // map index to result
}
impl Display for RunnerState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let summary = count_statuses(self.statuses.values());

        write!(f, "jobs={} summary={summary:?}", self.statuses.len())
    }
}

#[cfg(test)]
mod tests {
    use crate::status::{Satisfying, Unsatisfying};

    use super::*;

    fn is_all_settled(statuses: &HashMap<String, Status>) -> bool {
        statuses.values().all(Status::is_settled)
    }

    #[test]
    fn display_fmt() {
        let state = RunnerState {
            statuses: HashMap::from([
                (
                    String::from("a"),
                    Status::Unsatisfying(Unsatisfying::Blocked),
                ),
                (String::from("b"), Status::Satisfying(Satisfying::default())),
                (
                    String::from("c"),
                    Status::Unsatisfying(Unsatisfying::Blocked),
                ),
                (
                    String::from("d"),
                    Status::Unsatisfying(Unsatisfying::Skipped),
                ),
                (
                    String::from("e"),
                    Status::Unsatisfying(Unsatisfying::Blocked),
                ),
                (String::from("f"), Status::Unsatisfying(Unsatisfying::Error)),
            ]),
            ..Default::default()
        };
        let want = r#"jobs=6 summary={"blocked": 3, "changed": 1, "error": 1, "skipped": 1}"#;

        let got = &format!("{}", &state);

        assert_eq!(got, want);
    }

    #[test]
    fn is_all_settled_with_all_settled() {
        let state = RunnerState {
            statuses: HashMap::from([
                (
                    String::from("a"),
                    Status::Unsatisfying(Unsatisfying::Blocked),
                ),
                (String::from("b"), Status::Satisfying(Satisfying::default())),
                (
                    String::from("c"),
                    Status::Unsatisfying(Unsatisfying::Skipped),
                ),
                (String::from("d"), Status::Unsatisfying(Unsatisfying::Error)),
            ]),
            ..Default::default()
        };

        assert!(is_all_settled(&state.statuses));
    }

    #[test]
    fn is_all_settled_with_no_jobs() {
        let got = RunnerState {
            jobs: HashMap::new(),
            statuses: HashMap::new(),
        };

        assert!(is_all_settled(&got.statuses));
    }

    #[test]
    fn is_all_settled_with_some_not_settled() {
        let state = RunnerState {
            statuses: HashMap::from([
                (
                    String::from("a"),
                    Status::Unsatisfying(Unsatisfying::Blocked),
                ),
                (String::from("b"), Status::Satisfying(Satisfying::default())),
                (String::from("c"), Status::InProgress),
                (
                    String::from("d"),
                    Status::Unsatisfying(Unsatisfying::Skipped),
                ),
                (String::from("e"), Status::Waiting),
                (String::from("f"), Status::Unsatisfying(Unsatisfying::Error)),
            ]),
            ..Default::default()
        };

        assert!(!is_all_settled(&state.statuses));
    }
}
