use std::any::Any;
use std::fmt::Debug;
use std::process::Command;
pub trait Capability {}

pub trait CommandCapability: Capability {
    fn execute_command(&self, command: &str, args: &Vec<String>) -> Result<String, String>;
}

pub trait KubernetesCapability: Capability {
    fn apply_manifest(&self, manifest: &str) -> Result<(), String>;
    fn get_resource(&self, resource_type: &str, name: &str) -> Result<String, String>;
}

pub trait Topology {
    fn name(&self) -> &str;
}

pub trait Interpret<T: Topology> {
    fn execute(&self, topology: &T) -> Result<String, String>;
}

// --- Score Definition Structs (Concrete) ---
// CommandScore struct remains the same
#[derive(Debug, Clone)] // Added Debug/Clone for easier handling
pub struct CommandScore {
    name: String,
    command: String,
    args: Vec<String>,
}

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

// K8sResourceScore struct remains the same
#[derive(Debug, Clone)]
pub struct K8sResourceScore {
    name: String,
    manifest: String,
}

impl K8sResourceScore {
    pub fn new(name: String, manifest: String) -> Self {
        Self { name, manifest }
    }
}


// --- Metadata / Base Score Trait (Non-Generic) ---
// Trait for common info and enabling downcasting later if needed
pub trait ScoreDefinition: Debug + Send + Sync {
    fn name(&self) -> &str;
    // Method to allow downcasting
    fn as_any(&self) -> &dyn Any;
    // Optional: Could add methods for description, parameters etc.
    // fn description(&self) -> &str;

    // Optional but potentially useful: A way to clone the definition
    fn box_clone(&self) -> Box<dyn ScoreDefinition>;
}

// Implement Clone for Box<dyn ScoreDefinition>
impl Clone for Box<dyn ScoreDefinition> {
    fn clone(&self) -> Self {
        self.box_clone()
    }
}

// Implement ScoreDefinition for your concrete score types
impl ScoreDefinition for CommandScore {
    fn name(&self) -> &str {
        &self.name
    }
    fn as_any(&self) -> &dyn Any {
        self
    }
    fn box_clone(&self) -> Box<dyn ScoreDefinition> {
        Box::new(self.clone())
    }
}

impl ScoreDefinition for K8sResourceScore {
    fn name(&self) -> &str {
        &self.name
    }
    fn as_any(&self) -> &dyn Any {
        self
    }
     fn box_clone(&self) -> Box<dyn ScoreDefinition> {
        Box::new(self.clone())
    }
}


// --- Score Compatibility Trait (Generic over T) ---
// This remains largely the same, ensuring compile-time checks
pub trait Score<T: Topology>: ScoreDefinition {
    // No need for name() here, it's in ScoreDefinition
    fn compile(&self) -> Result<Box<dyn Interpret<T>>, String>;
}

// --- Implementations of Score<T> (Crucial Link) ---

// CommandScore implements Score<T> for any T with CommandCapability
impl<T> Score<T> for CommandScore
where
    T: Topology + CommandCapability + 'static, // Added 'static bound often needed for Box<dyn>
    // Self: ScoreDefinition // This bound is implicit now
{
    fn compile(&self) -> Result<Box<dyn Interpret<T>>, String> {
        // Pass necessary data from self to CommandInterpret
        Ok(Box::new(CommandInterpret {
            command: self.command.clone(),
            args: self.args.clone(),
        }))
    }
}

// K8sResourceScore implements Score<T> for any T with KubernetesCapability
impl<T> Score<T> for K8sResourceScore
where
    T: Topology + KubernetesCapability + 'static,
    // Self: ScoreDefinition
{
    fn compile(&self) -> Result<Box<dyn Interpret<T>>, String> {
        Ok(Box::new(K8sResourceInterpret {
            manifest: self.manifest.clone(), // Pass needed data
        }))
    }
}


// --- Interpret Implementations ---
// Need to hold the actual data now

struct CommandInterpret {
   command: String,
   args: Vec<String>, // Or owned Strings if lifetime is tricky
}

impl<'a, T> Interpret<T> for CommandInterpret
where
    T: Topology + CommandCapability,
{
    fn execute(&self, topology: &T) -> Result<String, String> {
         // Now uses data stored in self
        topology.execute_command(&self.command, &self.args)
    }
}

struct K8sResourceInterpret {
    manifest: String,
}

impl<T: Topology + KubernetesCapability> Interpret<T> for K8sResourceInterpret {
    fn execute(&self, topology: &T) -> Result<String, String> {
        topology.apply_manifest(&self.manifest)?;
        // apply_manifest returns Result<(), String>, adapt if needed
        Ok(format!("Applied manifest for {}", topology.name())) // Example success message
    }
}

// --- Maestro ---
// Maestro remains almost identical, leveraging the Score<T> bound
pub struct Maestro<T: Topology> {
    topology: T,
    // Stores Score<T> trait objects, ensuring compatibility
    scores: Vec<Box<dyn Score<T>>>,
}

impl<T: Topology + 'static> Maestro<T> { // Often need T: 'static here
    pub fn new(topology: T) -> Self {
        Self {
            topology,
            scores: Vec::new(),
        }
    }

    // This method signature is key - it takes a concrete S
    // and the compiler checks if S implements Score<T>
    pub fn register_score<S>(&mut self, score: S) -> Result<(), String>
    where
        S: Score<T> + ScoreDefinition + Clone + 'static, // Ensure S is a Score for *this* T
        // We might need S: Clone if we want to store Box::new(score)
        // Alternatively, accept Box<dyn ScoreDefinition> and try to downcast/wrap
    {
        println!(
            "Registering score '{}' for topology '{}'",
            score.name(),
            self.topology.name()
        );
        // The compiler has already guaranteed that S implements Score<T>
        // We need to box it as dyn Score<T>
        self.scores.push(Box::new(score));
        Ok(())
    }

     // Alternative registration if you have Box<dyn ScoreDefinition>
     pub fn register_score_definition(&mut self, score_def: Box<dyn ScoreDefinition>) -> Result<(), String>
     where
         T: Topology + CommandCapability + KubernetesCapability + 'static, // Example: list all needed caps here, or use generics + downcasting
     {
         println!(
             "Attempting to register score '{}' for topology '{}'",
             score_def.name(),
             self.topology.name()
         );

         // Downcast to check concrete type and then check compatibility
         if let Some(cs) = score_def.as_any().downcast_ref::<CommandScore>() {
             // Check if T satisfies CommandScore's requirements (CommandCapability)
             // This check is somewhat manual or needs restructuring if we avoid listing all caps
             // A simpler way is to just try to create the Box<dyn Score<T>>
             let boxed_score: Box<dyn Score<T>> = Box::new(cs.clone()); // This relies on the blanket impls
             self.scores.push(boxed_score);
             Ok(())
         } else if let Some(ks) = score_def.as_any().downcast_ref::<K8sResourceScore>() {
              // Check if T satisfies K8sResourceScore's requirements (KubernetesCapability)
             let boxed_score: Box<dyn Score<T>> = Box::new(ks.clone());
             self.scores.push(boxed_score);
             Ok(())
         } else {
              Err(format!("Score '{}' is of an unknown type or incompatible", score_def.name()))
         }
         // This downcasting approach in Maestro slightly undermines the full compile-time
         // check unless designed carefully. The generic `register_score<S: Score<T>>` is safer.
     }


    pub fn orchestrate(&self) -> Result<(), String> {
        println!("Orchestrating topology '{}'", self.topology.name());
        for score in &self.scores {
            println!("Compiling score '{}'", score.name()); // Use name() from ScoreDefinition
            let interpret = score.compile()?;
            println!("Executing score '{}'", score.name());
            interpret.execute(&self.topology)?;
        }
        Ok(())
    }
}

// --- TUI Example ---
struct ScoreItem {
    // Holds the definition/metadata, NOT the Score<T> trait object
    definition: Box<dyn ScoreDefinition>,
}

struct HarmonyTui {
    // List of available score *definitions*
    available_scores: Vec<ScoreItem>,
    // Example: Maybe maps topology names to Maestros
    // maestros: HashMap<String, Box<dyn Any>>, // Storing Maestros generically is another challenge!
}

impl HarmonyTui {
    fn new() -> Self {
        HarmonyTui { available_scores: vec![] }
    }

    fn add_available_score(&mut self, score_def: Box<dyn ScoreDefinition>) {
         self.available_scores.push(ScoreItem { definition: score_def });
    }

    fn display_scores(&self) {
        println!("Available Scores:");
        for (i, item) in self.available_scores.iter().enumerate() {
            println!("{}: {}", i, item.definition.name());
        }
    }

    fn execute_score(&self, score: ScoreItem) {
        score.definition.

    }

     // Example: Function to add a selected score to a specific Maestro
     // This function would need access to the Maestros and handle the types
     fn add_selected_score_to_maestro<T>(
         &self,
         score_index: usize,
         maestro: &mut Maestro<T>
     ) -> Result<(), String>
     where
         T: Topology + CommandCapability + KubernetesCapability + 'static, // Adjust bounds as needed
     {
         let score_item = self.available_scores.get(score_index)
             .ok_or("Invalid score index")?;

         // We have Box<dyn ScoreDefinition>, need to add to Maestro<T>
         // Easiest is to downcast and call the generic register_score

         if let Some(cs) = score_item.definition.as_any().downcast_ref::<CommandScore>() {
             // Compiler checks if CommandScore: Score<T> via register_score's bound
             maestro.register_score(cs.clone())?;
             Ok(())
         } else if let Some(ks) = score_item.definition.as_any().downcast_ref::<K8sResourceScore>() {
              // Compiler checks if K8sResourceScore: Score<T> via register_score's bound
             maestro.register_score(ks.clone())?;
             Ok(())
         } else {
             Err(format!("Cannot add score '{}': Unknown type or check Maestro compatibility", score_item.definition.name()))
         }
     }
}

pub struct K3DTopology {
    name: String,
    linux_host: LinuxHostTopology,
    cluster_name: String,
}

impl Capability for K3DTopology {}

impl K3DTopology {
    pub fn new(name: String, linux_host: LinuxHostTopology, cluster_name: String) -> Self {
        Self {
            name,
            linux_host,
            cluster_name,
        }
    }
}

impl Topology for K3DTopology {
    fn name(&self) -> &str {
        &self.name
    }
}

impl CommandCapability for K3DTopology {
    fn execute_command(&self, command: &str, args: &Vec<String>) -> Result<String, String> {
        self.linux_host.execute_command(command, args)
    }
}

impl KubernetesCapability for K3DTopology {
    fn apply_manifest(&self, manifest: &str) -> Result<(), String> {
        println!("Applying manifest to K3D cluster '{}'", self.cluster_name);
        // Write manifest to a temporary file
        let temp_file = format!("/tmp/manifest-harmony-temp.yaml");

        // Use the linux_host directly to avoid capability trait bounds
        self.linux_host
            .execute_command("bash", &Vec::from(["-c".to_string(), format!("cat > {}", temp_file)]))?;

        // Apply with kubectl
        self.linux_host.execute_command("kubectl", &Vec::from([
            "--context".to_string(),
            format!("k3d-{}", self.cluster_name),
            "apply".to_string(),
            "-f".to_string(),
            temp_file.to_string(),
        ]))?;

        Ok(())
    }

    fn get_resource(&self, resource_type: &str, name: &str) -> Result<String, String> {
        println!(
            "Getting resource {}/{} from K3D cluster '{}'",
            resource_type, name, self.cluster_name
        );
        self.linux_host.execute_command("kubectl", &Vec::from([
            "--context".to_string(),
            format!("k3d-{}", self.cluster_name),
            "get".to_string(),
            resource_type.to_string(),
            name.to_string(),
            "-o".to_string(),
            "yaml".to_string(),
        ]))
    }
}


pub struct LinuxHostTopology {
    name: String,
    host: String,
}
impl Capability for LinuxHostTopology {}

impl LinuxHostTopology {
    pub fn new(name: String, host: String) -> Self {
        Self { name, host }
    }
}

impl Topology for LinuxHostTopology {
    fn name(&self) -> &str {
        &self.name
    }
}

impl CommandCapability for LinuxHostTopology {
    fn execute_command(&self, command: &str, args: &Vec<String>) -> Result<String, String> {
        println!("Executing on {}: {} {:?}", self.host, command, args);
        // In a real implementation, this would SSH to the host and execute the command
        let output = Command::new(command)
            .args(args)
            .output()
            .map_err(|e| e.to_string())?;

        if output.status.success() {
            Ok(String::from_utf8_lossy(&output.stdout).to_string())
        } else {
            Err(String::from_utf8_lossy(&output.stderr).to_string())
        }
    }
}



// --- Main Function Adapated ---
fn main() {
    // --- Linux Host ---
    let linux_host = LinuxHostTopology::new("dev-machine".to_string(), "localhost".to_string());
    let mut linux_maestro = Maestro::new(linux_host);

    let df_score = CommandScore::new(
        "check-disk".to_string(),
        "df".to_string(),
        vec!["-h".to_string()],
    );

    // Registration uses the generic method, compiler checks CommandScore: Score<LinuxHostTopology>
    linux_maestro.register_score(df_score.clone()).unwrap(); // clone needed if df_score used later

    // --- K3D Host ---
    let k3d_host = LinuxHostTopology::new("k3d-host".to_string(), "localhost".to_string());
    let k3d_topology = K3DTopology::new(
        "dev-cluster".to_string(),
        k3d_host,
        "devcluster".to_string(),
    );
    let mut k3d_maestro = Maestro::new(k3d_topology);

    let nodes_score = CommandScore::new(
        "check-nodes".to_string(),
        "kubectl".to_string(),
        vec!["get".to_string(), "nodes".to_string()],
    );
    let nginx_score = K8sResourceScore::new(
        "deploy-nginx".to_string(),
        // ... manifest string ...
        r#"..."#.to_string(),
    );

    // Compiler checks CommandScore: Score<K3DTopology>
    k3d_maestro.register_score(nodes_score.clone()).unwrap();
    // Compiler checks K8sResourceScore: Score<K3DTopology>
    k3d_maestro.register_score(nginx_score.clone()).unwrap();


    // --- TUI Example Usage ---
    let mut tui = HarmonyTui::new();
    // Add score *definitions* to the TUI
    tui.add_available_score(Box::new(df_score));
    tui.add_available_score(Box::new(nodes_score));
    tui.add_available_score(Box::new(nginx_score));

    tui.display_scores();

    // Simulate user selecting score 0 (check-disk) and adding to linux_maestro
    match tui.add_selected_score_to_maestro(0, &mut linux_maestro) {
        Ok(_) => println!("Successfully registered check-disk to linux_maestro via TUI selection"),
        Err(e) => println!("Failed: {}", e), // Should succeed
    }

     // Simulate user selecting score 2 (deploy-nginx) and adding to linux_maestro
    match tui.add_selected_score_to_maestro(2, &mut linux_maestro) {
        Ok(_) => println!("Successfully registered deploy-nginx to linux_maestro via TUI selection"), // Should fail!
        Err(e) => println!("Correctly failed to add deploy-nginx to linux_maestro: {}", e),
        // The failure happens inside add_selected_score_to_maestro because the
        // maestro.register_score(ks.clone()) call fails the trait bound check
        // K8sResourceScore: Score<LinuxHostTopology> is false.
    }

     // Simulate user selecting score 2 (deploy-nginx) and adding to k3d_maestro
     match tui.add_selected_score_to_maestro(2, &mut k3d_maestro) {
         Ok(_) => println!("Successfully registered deploy-nginx to k3d_maestro via TUI selection"), // Should succeed
         Err(e) => println!("Failed: {}", e),
     }

    // --- Orchestration ---
    println!("\n--- Orchestrating Linux Maestro ---");
    linux_maestro.orchestrate().unwrap();
    println!("\n--- Orchestrating K3D Maestro ---");
    k3d_maestro.orchestrate().unwrap();
}
