use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
use std::fs;

#[derive(Serialize, Deserialize, Debug)]
pub struct Config {
    #[serde(flatten)]
    pub repos: HashMap<String, RepoConfig>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RepoConfig {
    pub remotes: HashMap<String, String>,
    pub merges: Vec<MergeConfig>,
    pub target: Option<TargetConfig>,
    pub fetch_all: Option<Vec<String>>,
    pub sparse: Option<Vec<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct TargetConfig {
    pub remote: String,
    pub branch: String,
}

#[derive(Debug, Serialize, Clone)]
pub struct MergeConfig {
    pub remote: String,
    pub reference: String,
    pub depth: Option<u32>,
}

// Custom deserializer to handle both string ("oca 18.0") and object format
impl<'de> Deserialize<'de> for MergeConfig {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct RawMerge {
            remote: Option<String>,
            #[serde(rename = "ref")]
            reference: Option<String>,
            depth: Option<u32>,
        }

        let v = serde_yaml::Value::deserialize(deserializer)?;
        if let Some(s) = v.as_str() {
            let parts: Vec<&str> = s.split_whitespace().collect();
            if parts.len() < 2 {
                return Err(serde::de::Error::custom(format!(
                    "Invalid merge string format: '{}'. Expected 'remote ref'",
                    s
                )));
            }
            Ok(MergeConfig {
                remote: parts[0].to_string(),
                reference: parts[1..].join(" "),
                depth: None,
            })
        } else {
            let raw = RawMerge::deserialize(v).map_err(serde::de::Error::custom)?;
            let remote = raw
                .remote
                .ok_or_else(|| serde::de::Error::custom("Missing 'remote' field"))?;
            let reference = raw
                .reference
                .ok_or_else(|| serde::de::Error::custom("Missing 'ref' field"))?;
            Ok(MergeConfig {
                remote,
                reference,
                depth: raw.depth,
            })
        }
    }
}

pub fn read_yaml() -> Config {
    let contents = fs::read_to_string("./dv-aggregator.yaml")
        .expect("./dv-aggregator.yaml file not found in current directory, aborting");

    let config: Config = match serde_yaml::from_str(&contents) {
        Ok(cfg) => cfg,
        Err(error) => {
            panic!("Invalid configuration detected: {}", error);
        }
    };

    config
}
