// SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
// SPDX-License-Identifier: BSD-2-Clause
//! A unit test suite for the `utf8-locale` library.

#![allow(clippy::panic_in_result_fn, reason = "test suite")]

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::fs;
use std::io::Error as IoError;
use std::sync::LazyLock;

use anyhow::{Context as _, Result as AnyResult, anyhow};
use serde_derive::Deserialize;
use serde_json::Error as SerdeJsonError;

use super::LanguagesDetect;

#[derive(Debug)]
enum TestError {
    FileRead(String, IoError),

    FileParse(String, SerdeJsonError),
}

impl Display for TestError {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match *self {
            Self::FileRead(ref fname, _) => write!(f, "Could not read {fname}"),
            Self::FileParse(ref fname, _) => write!(f, "Could not parse {fname}"),
        }
    }
}

impl StdError for TestError {
    #[inline]
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match *self {
            Self::FileRead(_, ref err) => Some(err),
            Self::FileParse(_, ref err) => Some(err),
        }
    }
}

#[derive(Debug, Deserialize)]
struct TDataLang {
    env: HashMap<String, String>,
    expected: Vec<String>,
}

#[derive(Debug, Deserialize)]
struct TData {
    languages: Vec<TDataLang>,
}

fn load_test_data() -> Result<TData, TestError> {
    let fname = format!(
        "{cargo_dir}/tests/data.json",
        cargo_dir = env!("CARGO_MANIFEST_DIR")
    );
    let contents: String =
        fs::read_to_string(&fname).map_err(|err| TestError::FileRead(fname.clone(), err))?;
    serde_json::from_str(&contents).map_err(|err| TestError::FileParse(fname, err))
}

fn get_test_data() -> AnyResult<&'static TData> {
    static TEST_DATA: LazyLock<Result<TData, TestError>> = LazyLock::new(load_test_data);
    TEST_DATA
        .as_ref()
        .map_err(|err| anyhow!("Could not load the test data: {err}"))
}

#[test]
fn preferred() -> AnyResult<()> {
    for tcase in &get_test_data()?.languages {
        assert_eq!(
            LanguagesDetect::new()
                .with_env(tcase.env.clone())
                .detect()
                .with_context(|| format!(
                    "Could not determine the list of preferred languages for {tcase:?}"
                ))?,
            tcase.expected
        );
    }
    Ok(())
}
