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

use std::collections::HashMap;

use anyhow::Error as AnyError;
use handlebars::{RenderError, TemplateError};
use serde::{Deserialize, Serialize};
use serde_json::Error as JsonError;
use thiserror::Error;
use typed_format_version::LoadError as TypedError;

/// An error that occurred during the preparation of the test data.
#[derive(Debug, Error)]
#[non_exhaustive]
#[allow(clippy::error_impl_error)]
pub enum Error {
    /// Could not read, write, examine, etc. a file.
    #[error("An I/O error occurred")]
    IO(#[source] AnyError),

    /// Could not load the test data.
    #[error("Could not parse the test data file")]
    ParseJson(#[source] JsonError),

    /// Could not load the handlebars template.
    #[error("Could not parse the output template file")]
    ParseTemplate(#[source] TemplateError),

    /// Could not render a handlebars template.
    #[error("Could not render the {0} template using the {1} data")]
    Render(String, String, #[source] RenderError),

    /// Could not parse the data file's format version.
    #[error("Could not parse the data format version")]
    VersionLoad(#[source] TypedError),

    /// Unknown format version for the data file.
    #[error("Unsupported data format version {0}.{1}")]
    VersionUnknown(u32, u32),
}

/// Runtime configuration for the feature-check test generator.
#[derive(Debug)]
pub struct Config {
    /// The path to the JSON test data file.
    pub data_path: String,
    /// The path to the output file to generate.
    pub output_path: String,
    /// The path to the Handlebars template to use.
    pub template_path: String,
    /// Verbose operation (diagnostic output).
    pub verbose: bool,
}

/// The version of a test data's format specification.
#[derive(Debug, Deserialize, Serialize)]
pub struct TestFormatVersion {
    /// The version major number.
    pub major: u32,
    /// The version minor number.
    pub minor: u32,
}

/// Metadata about a test data file's format.
#[derive(Debug, Deserialize, Serialize)]
pub struct TestFormat {
    /// The version of the test format specification.
    pub version: TestFormatVersion,
}

/// A synonym for a comparison operator.
#[derive(Debug, Deserialize, Serialize)]
pub struct TestOpSynonym {
    /// The word name of the comparison operator.
    pub name: String,
    /// The symbol to use for the comparison operator.
    pub sym: String,
}

/// Test data for a single simple expression check.
#[derive(Debug, Deserialize, Serialize)]
pub struct TestSimpleCompare {
    /// The feature to compare against ("base" or "beta").
    pub name: String,
    /// The comparison operator.
    pub op: String,
    /// The version to compare against.
    pub version: String,
    /// The comparison result.
    pub result: bool,
}

/// Test data for simple expression checks.
#[derive(Debug, Deserialize, Serialize)]
pub struct SimpleTestData {
    /// The base version to compare against.
    pub features: HashMap<String, String>,
    /// The synonyms to define for the comparison operators.
    pub synonyms: Vec<TestOpSynonym>,
    /// The versions to compare against.
    pub compare: Vec<TestSimpleCompare>,
}

/// Test data parsed out of an input file.
#[derive(Debug, Deserialize, Serialize)]
pub struct TestData {
    /// The input file's metadata format info.
    pub format: TestFormat,
    /// The simple expression tests to run.
    pub simple: SimpleTestData,
}

/// The name of the template to use within the Handlebars renderer.
pub const TEMPLATE_NAME: &str = "test";
