//! Load `shakrs.json`, resolve which policies run with what params, and validate
//! that every override names a registered policy.
//!
//! Enablement is opt-out: a registered policy runs unless an override turns it
//! off. The parser owns the file format; the registry owns the policy set; this
//! module joins them.

use std::collections::BTreeMap;
use std::path::Path;

use shakrs_json_parser::{PolicyOverride, parse};

use crate::runtime::fs;
use crate::types::{ConfigError, Registry, ResolvedConfig};

impl core::fmt::Display for ConfigError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Missing => write!(f, "shakrs.json not found at the workspace root"),
            Self::Io(detail) => write!(f, "could not read shakrs.json: {detail}"),
            Self::Parse(detail) => write!(f, "{detail}"),
            Self::UnknownPolicy(id) => {
                write!(f, "override names unregistered policy `{id}`")
            }
        }
    }
}

/// The `shakrs.json` file name, relative to the workspace root.
const CONFIG_FILE: &str = "shakrs.json";

/// Load and resolve the config at `workspace_root` against `registry`.
///
/// # Errors
///
/// Returns [`ConfigError::Missing`] when the file is absent, [`ConfigError::Io`]
/// on a read failure, [`ConfigError::Parse`] on a parse/schema/semantic error,
/// and [`ConfigError::UnknownPolicy`] when an override names an unregistered
/// policy.
pub(crate) fn load_and_resolve(
    workspace_root: &Path,
    registry: &Registry,
) -> Result<ResolvedConfig, ConfigError> {
    let path = workspace_root.join(CONFIG_FILE);
    let bytes = match fs::read(&path) {
        Ok(Some(bytes)) => bytes,
        Ok(None) => return Err(ConfigError::Missing),
        Err(err) => return Err(ConfigError::Io(err.to_string())),
    };

    let config = parse(&bytes).map_err(|err| ConfigError::Parse(err.to_string()))?;

    for id in config.policies.overrides.keys() {
        if !registry.policies.contains_key(id.as_str()) {
            return Err(ConfigError::UnknownPolicy(id.clone()));
        }
    }

    let mut enabled: BTreeMap<String, serde_json::Value> = BTreeMap::new();
    for id in registry.policies.keys() {
        let (is_enabled, params) = match config.policies.overrides.get(*id) {
            Some(PolicyOverride::Enabled(flag)) => (*flag, serde_json::Value::Null),
            Some(PolicyOverride::Detailed {
                enabled: flag,
                params,
            }) => (*flag, params.clone()),
            None => (config.policies.default_enabled, serde_json::Value::Null),
        };
        if is_enabled {
            let _ = enabled.insert((*id).to_owned(), params);
        }
    }

    Ok(ResolvedConfig {
        enabled,
        waivers: config.waivers,
    })
}
