//! Summary report of all test runs so far, as HTML.

use std::{collections::HashMap, path::Path, time::SystemTime};

use html_page::{Element, HtmlPage, Tag};
use log::trace;

use crate::{
    run::commit_dir,
    {runlog::RunLog, spec::Spec, timestamp},
};

const REFRESH_INTERVAL: &str = "300";

/// A summary report of all test runs.
pub struct Report {
    description: String,
    commits: Vec<(String, bool)>,
}

impl Report {
    /// Create a new [`Report`].
    pub fn new(description: &str, stats_txt: &Path) -> anyhow::Result<Self> {
        let text = std::fs::read(stats_txt)?;
        let text = String::from_utf8_lossy(&text);
        let mut commits = vec![];
        for line in text.lines() {
            let mut words = line.split_whitespace();
            let commit = words.next().unwrap();
            let result = words.next().unwrap();
            commits.push((commit.to_string(), result == "SUCCESS"));
        }
        trace!("loaded {} commits", commits.len());
        Ok(Self {
            description: description.into(),
            commits,
        })
    }

    fn count(&self) -> HashMap<String, (usize, usize)> {
        let mut counts = HashMap::new();
        for (commit, success) in self.commits.iter() {
            let (mut s, mut f) = counts.get(commit).copied().unwrap_or((0, 0));
            if *success {
                s += 1;
            } else {
                f += 1;
            }
            counts.insert(commit.into(), (s, f));
        }
        counts
    }

    fn counts_with_date(
        spec: &Spec,
        working_dir: &Path,
        counts: HashMap<String, (usize, usize)>,
    ) -> HashMap<String, (String, usize, usize)> {
        let mut map = HashMap::new();
        let mut run_log = RunLog::default();
        for (commit, (successes, failures)) in counts.iter() {
            let date = spec.git_commit_date(working_dir, commit, &mut run_log);
            map.insert(commit.to_string(), (date, *successes, *failures));
        }
        map
    }

    /// Produce HTML report.
    pub fn as_html(&self, spec: &Spec, working_dir: &Path) -> HtmlPage {
        let counts = self.count();
        let counts = Self::counts_with_date(spec, working_dir, counts);

        let now = timestamp(&SystemTime::now());

        let mut table = Element::new(Tag::Table).with_child(
            Element::new(Tag::Tr)
                .with_child(Element::new(Tag::Th).with_text("date"))
                .with_child(Element::new(Tag::Th).with_text("commit"))
                .with_child(
                    Element::new(Tag::Th)
                        .with_class("numeric")
                        .with_text("successes"),
                )
                .with_child(
                    Element::new(Tag::Th)
                        .with_class("numeric")
                        .with_text("failures"),
                ),
        );

        let mut counts: Vec<_> = counts.iter().collect();
        counts.sort_by_cached_key(|(_, (date, _, _))| date);
        counts.reverse();

        for (commit, (date, successes, failures)) in counts.iter() {
            let successes = format!("{successes}");
            let failures = format!("{failures}");

            table.push_child(
                Element::new(Tag::Tr)
                    .with_child(Element::new(Tag::Td).with_class("date").with_text(date))
                    .with_child(
                        Element::new(Tag::Td).with_child(
                            Element::new(Tag::A)
                                .with_attribute("href", &commit_dir(commit))
                                .with_class("commit")
                                .with_text(commit),
                        ),
                    )
                    .with_child(
                        Element::new(Tag::Td)
                            .with_class("numeric")
                            .with_text(&successes),
                    )
                    .with_child(
                        Element::new(Tag::Td)
                            .with_class("numeric")
                            .with_text(&failures),
                    ),
            );
        }

        let total = self.commits.len();
        HtmlPage::default()
            .with_head_element(Element::new(Tag::Title).with_text("Wumpus hunter"))
            .with_head_element(Element::new(Tag::Style).with_text(crate::CSS))
            .with_head_element(
                Element::new(Tag::Meta)
                    .with_attribute("http-equiv", "refresh")
                    .with_attribute("content", REFRESH_INTERVAL),
            )
            .with_body_element(Element::new(Tag::H1).with_text("Wumpus hunter"))
            .with_body_element(Element::new(Tag::P).with_text(&self.description))
            .with_body_element(
                Element::new(Tag::P)
                    .with_text(&format!("Total of {total} test runs. Last updated "))
                    .with_text(&now),
            )
            .with_body_element(table)
    }
}
