use self::config_file::{find_config_file, Configs};
pub(crate) use cli::CliConfigs;

mod cli;
mod config_file;

/// this struct may hold all configuration
pub struct Config {
    pub evaluator: eval::Available,

    pub startup_msg: bool,
}

impl Config {
    /// Get the configs from all possible places (args, file, env...)
    pub fn read(args: &CliConfigs) -> Self {
        // let config = if let Some(config) = &args.config_path {
        //     Ok(config.clone())
        // } else {
        //     find_config_file()
        // };
        let config = args
            .config_path
            .clone()
            .ok_or(())
            .or_else(|()| find_config_file());

        // try to read a maybe existing config file
        let config = config.ok().and_then(|path| {
            Configs::read(path).map_or_else(
                |e| {
                    eprintln!("Config error: {e:?}");
                    eprintln!("Proceeding with defaults or cli args...");
                    None
                },
                Some,
            )
        });

        if let Some(file) = config {
            Self {
                evaluator: args.evaluator.and(file.evaluator).unwrap_or_default(),
                // this is negated because to an outward api, the negative is more intuitive,
                // while in the source the other way around is more intuitive
                startup_msg: !(args.no_startup_message || file.no_startup_message),
            }
        } else {
            Self {
                startup_msg: !args.no_startup_message,
                evaluator: args.evaluator.unwrap_or_default(),
            }
        }
    }
}

pub mod error {
    /// Errors that can occur when reading configs
    #[derive(Debug)]
    pub enum Config {
        /// The config dir doesn't exist
        NoConfigDir,
        /// We didn't find a config file in the config dir
        NoConfigFileFound,
        /// An io error happened while reading/opening it!
        IoError(std::io::Error),
        /// The given extension (via an argument) isn't known.
        ///
        /// Occurs if the extension is neither `.json` nor `.ron` (including if there is no extension, in which case this will be `None`).
        UnknownExtension(Option<String>),
        /// Wrapper around an `Error` from `serde_json`
        SerdeJsonError(serde_json::Error),
        /// Wrapper around a `SpannedError` from `ron`
        SerdeRonError(ron::error::SpannedError),
    }

    impl From<std::io::Error> for Config {
        fn from(value: std::io::Error) -> Self {
            Self::IoError(value)
        }
    }

    impl From<serde_json::Error> for Config {
        fn from(value: serde_json::Error) -> Self {
            Self::SerdeJsonError(value)
        }
    }

    impl From<ron::error::SpannedError> for Config {
        fn from(value: ron::error::SpannedError) -> Self {
            Self::SerdeRonError(value)
        }
    }
}
