// SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
// SPDX-License-Identifier: BSD-2-Clause
//! Test file output routines for the feature-check test generator.

use std::io::Write;
use std::path::Path;

use anyhow::Context;
use handlebars::Handlebars;
use tempfile::NamedTempFile;
use tracing::debug;

use crate::defs::{Config, Error, TestData, TEMPLATE_NAME};

/// Render the test data into the specified output file.
///
/// # Errors
///
/// Fails on template rendering errors or on filesystem errors when
/// writing out or renaming the temporary file.
pub fn write_output_file(
    cfg: &Config,
    template: &Handlebars<'_>,
    test_data: &TestData,
) -> Result<(), Error> {
    let output = template
        .render(TEMPLATE_NAME, &test_data)
        .map_err(|err| Error::Render(cfg.template_path.clone(), cfg.data_path.clone(), err))?;
    debug!(
        "About to write {count} characters to {output}",
        count = output.len(),
        output = cfg.output_path
    );

    let opath = Path::new(&cfg.output_path);
    let odir = opath
        .parent()
        .with_context(|| {
            format!(
                "Could not determine the parent directory of {output}",
                output = cfg.output_path
            )
        })
        .map_err(Error::IO)?;
    let mut tempf = NamedTempFile::new_in(odir)
        .with_context(|| {
            format!(
                "Could not create a temporary file in the {odir} directory",
                odir = odir.display()
            )
        })
        .map_err(Error::IO)?;
    let tempf_path = tempf.path().display().to_string();
    debug!("Using {tempf_path} as a temporary output file");
    tempf
        .write_all(output.as_bytes())
        .with_context(|| format!("Could not write to the {tempf_path} temporary file"))
        .map_err(Error::IO)?;
    debug!("Overwriting the output file");
    tempf
        .persist(&cfg.output_path)
        .with_context(|| {
            format!(
                "Could not rename the {tempf_path} temporary file to {output}",
                output = cfg.output_path
            )
        })
        .map_err(Error::IO)?;
    Ok(())
}
