// Basic traits from your example
trait Topology {}

trait Score: Clone + std::fmt::Debug {
    fn get_interpret<T: Topology>(&self) -> Box<dyn Interpret<T>>;
    fn name(&self) -> String;
}

trait Interpret<T: Topology> {
    fn execute(&self);
}

struct Maestro<T: Topology> {
    topology: T
}

impl<T: Topology> Maestro<T> {
    pub fn new(topology: T) -> Self {
        Maestro { topology }
    }
    
    pub fn register_score<S: Score + 'static>(&self, score: S) {
        println!("Registering score: {}", score.name());
    }
    
    pub fn execute_score<S: Score + 'static>(&self, score: S) {
        println!("Executing score: {}", score.name());
        score.get_interpret::<T>().execute();
    }
}

// Capability traits - these are used to enforce requirements
trait CommandExecution {
    fn execute_command(&self, command: &[String]) -> Result<String, String>;
}

trait FileSystem {
    fn read_file(&self, path: &str) -> Result<String, String>;
    fn write_file(&self, path: &str, content: &str) -> Result<(), String>;
}

// A concrete topology implementation
#[derive(Clone, Debug)]
struct LinuxHostTopology {
    hostname: String,
}

impl Topology for LinuxHostTopology {}

// Implement the capabilities for LinuxHostTopology
impl CommandExecution for LinuxHostTopology {
    fn execute_command(&self, command: &[String]) -> Result<String, String> {
        println!("Executing command on {}: {:?}", self.hostname, command);
        // In a real implementation, this would use std::process::Command
        Ok(format!("Command executed successfully on {}", self.hostname))
    }
}

impl FileSystem for LinuxHostTopology {
    fn read_file(&self, path: &str) -> Result<String, String> {
        println!("Reading file {} on {}", path, self.hostname);
        Ok(format!("Content of {} on {}", path, self.hostname))
    }

    fn write_file(&self, path: &str, content: &str) -> Result<(), String> {
        println!("Writing to file {} on {}: {}", path, self.hostname, content);
        Ok(())
    }
}

// Another topology that doesn't support command execution
#[derive(Clone, Debug)]
struct BareMetalTopology {
    device_id: String,
}

impl Topology for BareMetalTopology {}

impl FileSystem for BareMetalTopology {
    fn read_file(&self, path: &str) -> Result<String, String> {
        println!("Reading file {} on device {}", path, self.device_id);
        Ok(format!("Content of {} on device {}", path, self.device_id))
    }

    fn write_file(&self, path: &str, content: &str) -> Result<(), String> {
        println!("Writing to file {} on device {}: {}", path, self.device_id, content);
        Ok(())
    }
}

// CommandScore implementation
#[derive(Clone, Debug)]
struct CommandScore {
    name: String,
    args: Vec<String>,
}

impl CommandScore {
    pub fn new(name: String, args: Vec<String>) -> Self {
        CommandScore { name, args }
    }
}

impl Score for CommandScore {
    fn get_interpret<T: Topology + CommandExecution + 'static>(&self) -> Box<dyn Interpret<T>> {
        // This is the key part: we constrain T to implement CommandExecution
        // If T doesn't implement CommandExecution, this will fail to compile
        Box::new(CommandInterpret::<T>::new(self.clone()))
    }
    
    fn name(&self) -> String {
        self.name.clone()
    }
}

// CommandInterpret implementation
struct CommandInterpret<T: Topology + CommandExecution> {
    score: CommandScore,
    _marker: std::marker::PhantomData<T>,
}

impl<T: Topology + CommandExecution> CommandInterpret<T> {
    pub fn new(score: CommandScore) -> Self {
        CommandInterpret {
            score,
            _marker: std::marker::PhantomData,
        }
    }
}

impl<T: Topology + CommandExecution> Interpret<T> for CommandInterpret<T> {
    fn execute(&self) {
        println!("Command interpret is executing: {:?}", self.score.args);
        // In a real implementation, you would call the topology's execute_command method
        // topology.execute_command(&self.score.args);
    }
}

// FileScore implementation - a different type of score that requires FileSystem capability
#[derive(Clone, Debug)]
struct FileScore {
    name: String,
    path: String,
    content: Option<String>,
}

impl FileScore {
    pub fn new_read(name: String, path: String) -> Self {
        FileScore { name, path, content: None }
    }
    
    pub fn new_write(name: String, path: String, content: String) -> Self {
        FileScore { name, path, content: Some(content) }
    }
}

impl Score for FileScore {
    fn get_interpret<T: Topology>(&self) -> Box<dyn Interpret<T>> {
        // This constrains T to implement FileSystem
        Box::new(FileInterpret::<T>::new(self.clone()))
    }
    
    fn name(&self) -> String {
        self.name.clone()
    }
}

// FileInterpret implementation
struct FileInterpret<T: Topology + FileSystem> {
    score: FileScore,
    _marker: std::marker::PhantomData<T>,
}

impl<T: Topology + FileSystem> FileInterpret<T> {
    pub fn new(score: FileScore) -> Self {
        FileInterpret {
            score,
            _marker: std::marker::PhantomData,
        }
    }
}

impl<T: Topology + FileSystem> Interpret<T> for FileInterpret<T> {
    fn execute(&self) {
        match &self.score.content {
            Some(content) => {
                println!("File interpret is writing to {}: {}", self.score.path, content);
                // In a real implementation: topology.write_file(&self.score.path, content);
            },
            None => {
                println!("File interpret is reading from {}", self.score.path);
                // In a real implementation: let content = topology.read_file(&self.score.path);
            }
        }
    }
}

fn main() {
    // Create our topologies
    let linux = LinuxHostTopology { hostname: "server1.example.com".to_string() };
    let bare_metal = BareMetalTopology { device_id: "device001".to_string() };
    
    // Create our maestros
    let linux_maestro = Maestro::new(linux);
    let bare_metal_maestro = Maestro::new(bare_metal);
    
    // Create scores
    let command_score = CommandScore::new(
        "List Files".to_string(),
        vec!["ls".to_string(), "-la".to_string()]
    );
    
    let file_read_score = FileScore::new_read(
        "Read Config".to_string(),
        "/etc/config.json".to_string()
    );
    
    // This will work because LinuxHostTopology implements CommandExecution
    linux_maestro.execute_score(command_score.clone());
    
    // This will work because LinuxHostTopology implements FileSystem
    linux_maestro.execute_score(file_read_score.clone());
    
    // This will work because BareMetalTopology implements FileSystem
    bare_metal_maestro.execute_score(file_read_score);
    
    // This would NOT compile because BareMetalTopology doesn't implement CommandExecution:
    // bare_metal_maestro.execute_score(command_score);
    // The error would occur at compile time, ensuring type safety
    
    println!("All scores executed successfully!");
}
