mod junit;
mod log;
mod markdown;

use crate::prelude::*;

pub(crate) trait StoryReportGenerator {
    fn report_message(&self, message: &Message);
}

pub(crate) trait Generator {
    type StoryReportGenerator: StoryReportGenerator;
    /// Generate the report for a successful story.
    fn report_success(
        &self,
        story: &story::Story,
        duration: std::time::Duration,
    ) -> Self::StoryReportGenerator;
    /// Generate the report for a failed story.
    fn report_failure(
        &self,
        story: &story::Story,
        duration: std::time::Duration,
        reason: String,
    ) -> Self::StoryReportGenerator;
}

pub(crate) struct StoryGeneratorDispatcher {
    junit: Option<junit::StoryGenerator>,
    markdown: Option<markdown::StoryGenerator>,
    log: log::StoryGenerator,
}

impl StoryReportGenerator for StoryGeneratorDispatcher {
    fn report_message(&self, message: &Message) {
        if let Some(junit) = &self.junit {
            junit.report_message(message);
        }
        if let Some(markdown) = &self.markdown {
            markdown.report_message(message);
        }
        self.log.report_message(message);
    }
}

pub(crate) struct GeneratorDispatcher {
    junit: Option<junit::Generator>,
    markdown: Option<markdown::Generator>,
    log: log::Generator,
}

impl Generator for GeneratorDispatcher {
    type StoryReportGenerator = StoryGeneratorDispatcher;
    fn report_failure(
        &self,
        story: &story::Story,
        duration: std::time::Duration,
        reason: String,
    ) -> StoryGeneratorDispatcher {
        let junit = self
            .junit
            .as_ref()
            .map(|junit| junit.report_failure(story, duration.clone(), reason.clone()));
        let markdown = self
            .markdown
            .as_ref()
            .map(|markdown| markdown.report_failure(story, duration.clone(), reason.clone()));
        let log = self.log.report_failure(story, duration, reason);
        StoryGeneratorDispatcher {
            junit,
            markdown,
            log,
        }
    }
    fn report_success(
        &self,
        story: &story::Story,
        duration: std::time::Duration,
    ) -> StoryGeneratorDispatcher {
        let junit = self
            .junit
            .as_ref()
            .map(|junit| junit.report_success(story, duration.clone()));
        let markdown = self
            .markdown
            .as_ref()
            .map(|markdown| markdown.report_success(story, duration));
        let log = self.log.report_success(story, duration);
        StoryGeneratorDispatcher {
            junit,
            markdown,
            log,
        }
    }
}

impl GeneratorDispatcher {
    pub(crate) fn new(
        name: &String,
        junit_cfg: Option<&story::JunitConfiguration>,
        markdown_cfg: Option<&story::MarkdownConfiguration>,
    ) -> GeneratorDispatcher {
        let junit = junit_cfg.map(|config| junit::Generator::new(name, config));
        let markdown = markdown_cfg.map(|config| markdown::Generator::new(name, config));
        let log = log::Generator::new();
        GeneratorDispatcher {
            junit,
            markdown,
            log,
        }
    }
}
