use anyhow::Result;
use indexmap::IndexMap;
use serde::Deserialize;
use std::{fmt::Debug, fs::File, io::BufReader, path::Path};

#[derive(Debug, Deserialize)]
pub(crate) struct Plan {
    pub(crate) source: String,
    pub(crate) targets: IndexMap<String, TargetMapping>,
}

impl Plan {
    pub(crate) fn from_path(path: &Path) -> Result<Self> {
        let file = File::open(path)?;
        let reader = BufReader::new(file);
        let plan = serde_json::from_reader(reader)?;
        Ok(plan)
    }
}

#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub(crate) enum TargetMapping {
    Identity(Vec<String>),
    Mapped(IndexMap<String, Mapped>),
}

impl TargetMapping {
    pub(crate) fn iter(&self) -> Vec<(&str, &str)> {
        match self {
            TargetMapping::Identity(inputs) => {
                inputs.iter().map(|i| (i.as_str(), i.as_str())).collect()
            }
            TargetMapping::Mapped(mapping) => mapping
                .iter()
                .map(|(source_input, mapped)| {
                    (
                        source_input.as_str(),
                        match mapped {
                            Mapped::Named(target_input) => target_input.as_str(),
                            Mapped::Same(true) => source_input.as_str(),
                            _ => panic!("invalid mapped value"),
                        },
                    )
                })
                .collect(),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub(crate) enum Mapped {
    Named(String),
    Same(bool),
}
