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

use radicle::git::Oid;

#[derive(Default, Debug)]
pub struct RunLog {
    url: Option<String>,
    git_ref: Option<String>,
    git_commit: Option<Oid>,
    runcmds: Vec<RunCmdLog>,
}

impl RunLog {
    const TITLE: &'static str = "Wumpus run log";

    pub fn url(&mut self, url: &str) {
        self.url = Some(url.into());
    }

    pub fn git_ref(&mut self, git_ref: &str) {
        self.git_ref = Some(git_ref.into());
    }

    pub fn git_commit(&mut self, commit: Oid) {
        self.git_commit = Some(commit);
    }

    pub fn runcmd(&mut self, argv: &[&str], exit: i32, stdout: &str, stderr: &str) {
        self.runcmds.push(RunCmdLog {
            argv: argv.iter().map(|s| s.to_string()).collect(),
            exit,
            stdout: stdout.into(),
            stderr: stderr.into(),
        });
    }

    pub fn as_html(&self) -> HtmlPage {
        let url = if let Some(url) = &self.url {
            url.as_ref()
        } else {
            "unknown URL"
        };

        let git_ref = if let Some(git_ref) = &self.git_ref {
            Element::new(Tag::Code).with_text(git_ref)
        } else {
            Element::new(Tag::Span).with_text("unknown ref")
        };

        let commit = if let Some(commit) = &self.git_commit {
            Element::new(Tag::Code).with_text(&commit.to_string())
        } else {
            Element::new(Tag::Span).with_text("unknown commit")
        };

        let mut doc = HtmlPage::default()
            .with_head_element(Element::new(Tag::Title).with_text(Self::TITLE))
            .with_head_element(Element::new(Tag::Style).with_text(crate::CSS))
            .with_body_element(Element::new(Tag::H1).with_text(Self::TITLE))
            .with_body_element(Element::new(Tag::H2).with_text("Metadata"))
            .with_body_element(
                Element::new(Tag::Ul)
                    .with_child(
                        Element::new(Tag::Li).with_text("Repository: ").with_child(
                            Element::new(Tag::A)
                                .with_attribute("href", url)
                                .with_child(Element::new(Tag::Code).with_text(url)),
                        ),
                    )
                    .with_child(
                        Element::new(Tag::Li)
                            .with_text("Git ref: ")
                            .with_child(git_ref),
                    )
                    .with_child(
                        Element::new(Tag::Li)
                            .with_text("Commit: ")
                            .with_child(commit),
                    )
                    .with_child(
                        Element::new(Tag::Li)
                            .with_text("Wumpus hunter version: ")
                            .with_text(env!("CARGO_PKG_VERSION"))
                            .with_text(" commit ")
                            .with_text(env!("GIT_HEAD")),
                    ),
            );

        let mut toc = Element::new(Tag::Ul);
        let mut cmds = vec![];

        for (i, run) in self.runcmds.iter().enumerate() {
            let id = format!("run{i}");
            let argv_id = format!("run{i}-argv");
            let exit_id = format!("run{i}-exit");
            let stdout_id = format!("run{i}-stdout");
            let stderr_id = format!("run{i}-stderr");

            cmds.push(
                Element::new(Tag::H2)
                    .with_attribute("id", &id)
                    .with_text("Run: ")
                    .with_text(&run.heading()),
            );
            toc.push_child(
                Element::new(Tag::Li).with_child(
                    Element::new(Tag::A)
                        .with_attribute("href", &format!("#{id}"))
                        .with_text(&run.heading()),
                ),
            );

            let mut sub_toc = Element::new(Tag::Ul);

            cmds.push(
                Element::new(Tag::H3)
                    .with_text("Argv")
                    .with_attribute("id", &argv_id),
            );
            sub_toc.push_child(
                Element::new(Tag::Li).with_child(
                    Element::new(Tag::A)
                        .with_attribute("href", &format!("#{argv_id}"))
                        .with_text("Argv"),
                ),
            );
            let mut list = Element::new(Tag::Ol);
            for arg in run.argv.iter() {
                list.push_child(
                    Element::new(Tag::Li).with_child(Element::new(Tag::Code).with_text(arg)),
                );
            }
            cmds.push(list);

            cmds.push(
                Element::new(Tag::H3)
                    .with_text("Exit code")
                    .with_attribute("id", &exit_id),
            );
            sub_toc.push_child(
                Element::new(Tag::Li).with_child(
                    Element::new(Tag::A)
                        .with_attribute("href", &format!("#{exit_id}"))
                        .with_text("Exit code"),
                ),
            );
            cmds.push(Element::new(Tag::P).with_text(&format!("Exit code {}", run.exit)));

            if !run.stdout.is_empty() {
                cmds.push(
                    Element::new(Tag::H3)
                        .with_text("Output")
                        .with_attribute("id", &stdout_id),
                );
                sub_toc.push_child(
                    Element::new(Tag::Li).with_child(
                        Element::new(Tag::A)
                            .with_attribute("href", &format!("#{stdout_id}"))
                            .with_text("Output"),
                    ),
                );
                cmds.push(
                    Element::new(Tag::Blockquote)
                        .with_child(Element::new(Tag::Pre).with_text(&run.stdout)),
                );
            }

            if !run.stderr.is_empty() {
                cmds.push(
                    Element::new(Tag::H3)
                        .with_text("Stderr")
                        .with_attribute("id", &stderr_id),
                );
                sub_toc.push_child(
                    Element::new(Tag::Li).with_child(
                        Element::new(Tag::A)
                            .with_attribute("href", &format!("#{stderr_id}"))
                            .with_text("Stderr"),
                    ),
                );
                cmds.push(
                    Element::new(Tag::Blockquote)
                        .with_child(Element::new(Tag::Pre).with_text(&run.stderr)),
                );
            }

            toc.push_child(sub_toc);
        }

        doc.push_to_body(Element::new(Tag::H2).with_text("Contents"));
        doc.push_to_body(toc);
        for e in cmds {
            doc.push_to_body(e);
        }

        doc
    }
}

#[derive(Debug)]
struct RunCmdLog {
    argv: Vec<String>,
    exit: i32,
    stdout: String,
    stderr: String,
}

impl RunCmdLog {
    fn heading(&self) -> String {
        let mut h = String::new();
        for arg in self.argv.iter() {
            h.push_str(arg);
            h.push(' ');
        }
        h
    }
}
