use anyhow::{Context, Result};
use rave_engine::types::entries::{
    CodeTemplate, EARole, ExecutionEngine, ExecutorRules, InputRules,
};
use serde::{Deserialize, Serialize};
use std::{fs, path::PathBuf};

#[derive(Debug, Serialize, Deserialize)]
pub struct ExecutableAgreementLib {
    #[serde(default)]
    pub title: String,
    pub input_rules: InputRules,
    pub executor_rules: ExecutorRules,
    pub one_time_run: bool,
    pub aggregate_execution: bool,
    pub roles: Vec<EARole>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SystemTemplateConfig {
    pub template: CodeTemplate,
    pub agreements: Vec<ExecutableAgreementLib>,
}

impl SystemTemplateConfig {
    pub fn load(library_path: String) -> Result<Vec<SystemTemplateConfig>> {
        let path = PathBuf::from(library_path);
        let mut templates = Vec::new();

        // Read all directories in the library path
        for entry in fs::read_dir(path)? {
            let entry = entry?;
            if entry.file_type()?.is_dir() {
                // Each RAVE has its own directory with an execution_code.rhai file
                let template_path = entry.path().join("execution_code.rhai");
                if template_path.exists() {
                    let code = fs::read_to_string(&template_path)
                        .context(format!("Failed to read {}", template_path.display()))?;
                    let code = rmp_serde::encode::to_vec(&code).unwrap();
                    // Parse template metadata from comments
                    let input_signature = serde_json::from_str(&fs::read_to_string(
                        &entry.path().join("input_signature.json"),
                    )?)?;
                    // read a json file in the folder
                    let output_signature = serde_json::from_str(&fs::read_to_string(
                        &entry.path().join("output_signature.json"),
                    )?)?;
                    let template = CodeTemplate {
                        version: semver::Version::parse("0.1.0").unwrap().into(),
                        title: entry.file_name().to_string_lossy().to_string(),
                        execution_code: code,
                        execution_engine: ExecutionEngine::Rhai,
                        input_signature,
                        output_signature,
                    };

                    // read the folder agreements
                    let agreements_path = entry.path().join("agreements");
                    let mut agreements = Vec::new();
                    if fs::metadata(&agreements_path)?.is_dir() {
                        for entry in fs::read_dir(agreements_path)? {
                            let entry = entry?;
                            if let Ok(agreement) =
                                serde_json::from_str(&fs::read_to_string(&entry.path())?)
                            {
                                agreements.push(agreement);
                            }
                        }
                    }

                    templates.push(SystemTemplateConfig {
                        template,
                        agreements,
                    });
                }
            }
        }

        Ok(templates)
    }
}
