use std::{cell::RefCell, fs::File, path::PathBuf, rc::Rc};

use crate::prelude::*;

pub(super) struct StoryGenerator {
    test_case: junit_report::TestCaseBuilder,
    messages: RefCell<String>,
    test_suite: Rc<RefCell<junit_report::TestSuiteBuilder>>,
}

impl Drop for StoryGenerator {
    fn drop(&mut self) {
        self.test_case.set_system_out(&self.messages.borrow());
        self.test_suite
            .borrow_mut()
            .add_testcase(self.test_case.build());
    }
}

impl report_generators::StoryReportGenerator for StoryGenerator {
    fn report_message(&self, message: &Message) {
        let mut messages = self.messages.borrow_mut();
        *messages = format!(
            "{}{}:{}:{}:{}\n",
            messages, message.severity, message.filename, message.line, message.content
        );
    }
}

pub(super) struct Generator {
    test_suite: Rc<RefCell<junit_report::TestSuiteBuilder>>,
    filename: String,
}

impl report_generators::Generator for Generator {
    type StoryReportGenerator = StoryGenerator;
    fn report_failure(
        &self,
        story: &story::Story,
        duration: std::time::Duration,
        reason: String,
    ) -> StoryGenerator {
        let test_case = junit_report::TestCaseBuilder::failure(
            &story.name,
            junit_report::Duration::new(duration.as_secs() as i64, duration.subsec_nanos() as i32),
            "failure",
            &reason,
        );
        let messages = Default::default();
        let test_suite = self.test_suite.clone();
        StoryGenerator {
            test_case,
            messages,
            test_suite,
        }
    }
    fn report_success(
        &self,
        story: &story::Story,
        duration: std::time::Duration,
    ) -> StoryGenerator {
        let test_case = junit_report::TestCaseBuilder::success(
            &story.name,
            junit_report::Duration::new(duration.as_secs() as i64, duration.subsec_nanos() as i32),
        );
        let messages = Default::default();
        let test_suite = self.test_suite.clone();
        StoryGenerator {
            test_case,
            messages,
            test_suite,
        }
    }
}

impl Drop for Generator {
    fn drop(&mut self) {
        let path: PathBuf = (&self.filename).into();
        std::fs::create_dir_all(path.parent().unwrap()).unwrap();

        let mut file = File::create(&self.filename).unwrap();
        let r = junit_report::ReportBuilder::new()
            .add_testsuite(self.test_suite.borrow_mut().build())
            .build();
        r.write_xml(&mut file).unwrap();
    }
}

impl Generator {
    pub(super) fn new(name: &String, config: &story::JunitConfiguration) -> Generator {
        let test_suite = Rc::new(RefCell::new(junit_report::TestSuiteBuilder::new(name)));
        let filename = config.filename.clone();
        Generator {
            test_suite,
            filename,
        }
    }
}
