use super::RpcHandler;
use anyhow::{Context, Result};

impl RpcHandler {
    pub(super) async fn handle_container_install(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let orchestrator = self
            .orchestrator
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Container orchestrator not available (dev mode required)"))?;

        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let manifest_path = params
            .get("manifest_path")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing manifest_path"))?;

        // Validate manifest path: reject path traversal and paths outside apps/
        if manifest_path.contains("..") {
            return Err(anyhow::anyhow!(
                "Invalid manifest_path: path traversal not allowed"
            ));
        }
        let path = std::path::Path::new(manifest_path);
        if path.is_absolute() {
            let apps_dir = self.config.data_dir.join("apps");
            if !path.starts_with(&apps_dir) {
                return Err(anyhow::anyhow!(
                    "Invalid manifest_path: must be under the apps directory"
                ));
            }
        }

        // Load manifest
        let manifest_content = tokio::fs::read_to_string(manifest_path)
            .await
            .context("Failed to read manifest file")?;
        let manifest: archipelago_container::AppManifest = serde_yaml::from_str(&manifest_content)
            .context("Failed to parse manifest")?;

        let container_name = orchestrator
            .install_container(&manifest, manifest_path)
            .await
            .context("Failed to install container")?;

        Ok(serde_json::json!(container_name))
    }

    pub(super) async fn handle_container_start(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let orchestrator = self
            .orchestrator
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Container orchestrator not available (dev mode required)"))?;

        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let app_id = params
            .get("app_id")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing app_id"))?;

        orchestrator
            .start_container(app_id)
            .await
            .context("Failed to start container")?;

        Ok(serde_json::json!({ "status": "started" }))
    }

    pub(super) async fn handle_container_stop(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let orchestrator = self
            .orchestrator
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Container orchestrator not available (dev mode required)"))?;

        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let app_id = params
            .get("app_id")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing app_id"))?;

        orchestrator
            .stop_container(app_id)
            .await
            .context("Failed to stop container")?;

        Ok(serde_json::json!({ "status": "stopped" }))
    }

    pub(super) async fn handle_container_remove(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let orchestrator = self
            .orchestrator
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Container orchestrator not available (dev mode required)"))?;

        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let app_id = params
            .get("app_id")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing app_id"))?;
        let preserve_data = params
            .get("preserve_data")
            .and_then(|v| v.as_bool())
            .unwrap_or(false);

        orchestrator
            .remove_container(app_id, preserve_data)
            .await
            .context("Failed to remove container")?;

        Ok(serde_json::json!({ "status": "removed" }))
    }

    pub(super) async fn handle_container_list(&self) -> Result<serde_json::Value> {
        // Try to get containers from orchestrator first
        if let Some(orchestrator) = &self.orchestrator {
            if let Ok(containers) = orchestrator.list_containers().await {
                if !containers.is_empty() {
                    return Ok(serde_json::to_value(containers)?);
                }
            }
        }

        // Fallback: list containers directly via sudo podman (for bundled apps)
        let output = tokio::process::Command::new("sudo")
            .args(["podman", "ps", "-a", "--format", "json"])
            .output()
            .await
            .context("Failed to list containers via podman")?;

        if !output.status.success() {
            return Ok(serde_json::json!([]));
        }

        let stdout = String::from_utf8_lossy(&output.stdout);
        if stdout.trim().is_empty() {
            return Ok(serde_json::json!([]));
        }

        // Parse podman JSON output
        let podman_containers: Vec<serde_json::Value> = serde_json::from_str(&stdout)
            .unwrap_or_else(|_| Vec::new());

        // Convert to our ContainerStatus format
        let containers: Vec<serde_json::Value> = podman_containers
            .iter()
            .map(|c| {
                let state = c.get("State").and_then(|v| v.as_str()).unwrap_or("unknown");
                let mapped_state = match state.to_lowercase().as_str() {
                    "running" => "running",
                    "exited" => "exited",
                    "stopped" => "stopped",
                    "created" => "created",
                    "paused" => "paused",
                    _ => "unknown",
                };

                let name = c.get("Names").and_then(|v| v.as_array()).and_then(|a| a.first()).and_then(|v| v.as_str()).unwrap_or("");

                // Determine lan_address based on container name
                let lan_address = match name {
                    "bitcoin-knots" => Some("http://localhost:8334"),
                    "lnd" => Some("http://localhost:8081"),
                    "tailscale" => Some("http://localhost:8240"),
                    _ => None,
                };

                serde_json::json!({
                    "id": c.get("Id").and_then(|v| v.as_str()).unwrap_or(""),
                    "name": name,
                    "state": mapped_state,
                    "image": c.get("Image").and_then(|v| v.as_str()).unwrap_or(""),
                    "created": c.get("Created").and_then(|v| v.as_str()).unwrap_or(""),
                    "ports": c.get("Ports").and_then(|v| v.as_array()).map(|a|
                        a.iter().filter_map(|p| p.get("hostPort").and_then(|v| v.as_u64()).map(|p| p.to_string())).collect::<Vec<_>>()
                    ).unwrap_or_default(),
                    "lan_address": lan_address,
                })
            })
            .collect();

        Ok(serde_json::json!(containers))
    }

    pub(super) async fn handle_container_status(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let orchestrator = self
            .orchestrator
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Container orchestrator not available (dev mode required)"))?;

        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let app_id = params
            .get("app_id")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing app_id"))?;

        let status = orchestrator
            .get_container_status(app_id)
            .await
            .context("Failed to get container status")?;

        Ok(serde_json::to_value(status)?)
    }

    pub(super) async fn handle_container_logs(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let orchestrator = self
            .orchestrator
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Container orchestrator not available (dev mode required)"))?;

        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let app_id = params
            .get("app_id")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing app_id"))?;
        let lines = params
            .get("lines")
            .and_then(|v| v.as_u64())
            .unwrap_or(100) as u32;

        let logs = orchestrator
            .get_container_logs(app_id, lines)
            .await
            .context("Failed to get container logs")?;

        Ok(serde_json::to_value(logs)?)
    }

    /// Used by HTTP GET /api/container/logs (same logic as container-logs RPC).
    pub async fn get_container_logs_value(
        &self,
        app_id: &str,
        lines: u32,
    ) -> Result<serde_json::Value> {
        let orchestrator = self
            .orchestrator
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Container orchestrator not available (dev mode required)"))?;

        let logs = orchestrator
            .get_container_logs(app_id, lines)
            .await
            .context("Failed to get container logs")?;

        Ok(serde_json::to_value(logs)?)
    }

    pub(super) async fn handle_container_health(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let orchestrator = self
            .orchestrator
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Container orchestrator not available (dev mode required)"))?;

        // If app_id is provided, get health for that app
        if let Some(params) = params {
            if let Some(app_id) = params.get("app_id").and_then(|v| v.as_str()) {
                let health = orchestrator
                    .get_health_status(app_id)
                    .await
                    .context("Failed to get container health")?;
                return Ok(serde_json::json!({ app_id: health }));
            }
        }

        // Otherwise, get health for all containers
        let containers = orchestrator
            .list_containers()
            .await
            .context("Failed to list containers")?;

        let mut health_map = serde_json::Map::new();
        for container in containers {
            if let Some(app_id) = container.name.strip_prefix("archipelago-") {
                if let Some(app_id) = app_id.strip_suffix("-dev") {
                    match orchestrator.get_health_status(app_id).await {
                        Ok(health) => {
                            health_map.insert(app_id.to_string(), serde_json::Value::String(health));
                        }
                        Err(_) => {
                            health_map.insert(app_id.to_string(), serde_json::Value::String("unknown".to_string()));
                        }
                    }
                }
            }
        }

        Ok(serde_json::Value::Object(health_map))
    }
}
