#![allow(dead_code)]
use crate::ipc_types::{
    ModuleToOrchestrator, OrchestratorToModule, ServiceOperation, ServiceRequest,
};
use crate::logging::safe_log;
use crate::secret_client::SecretClient;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::sync::{
    Mutex,
    mpsc::{Receiver, Sender, channel},
    oneshot,
};

// Type of channel for sending requests to the orchestrator
type RequestChannel = Sender<(String, oneshot::Sender<String>)>;

// Response receivers for pending requests
type PendingResponses = Arc<Mutex<HashMap<String, oneshot::Sender<String>>>>;

// Global IPC channel for sending requests and receiving responses
lazy_static::lazy_static! {
    static ref IPC_CHANNEL: (Arc<Mutex<Option<RequestChannel>>>, PendingResponses) = {
        let pending_responses = Arc::new(Mutex::new(HashMap::new()));
        let channel = Arc::new(Mutex::new(None));
        (channel, pending_responses)
    };
}

/// Processes runtime IPC messages from the orchestrator over stdin.
///
/// This loop handles secret responses and rotation notifications by delegating
/// to the shared SecretClient, and exits cleanly on a shutdown command.
pub async fn process_ipc_messages() {
    let stdin = io::stdin();
    let mut reader = BufReader::new(stdin);
    let mut line = String::new();
    let client = SecretClient::global();

    // Create channel for sending requests
    let (tx, rx) = channel::<(String, oneshot::Sender<String>)>(100);
    {
        let mut channel = IPC_CHANNEL.0.lock().await;
        *channel = Some(tx);
    }

    // Spawn task to handle outgoing requests
    let stdout = io::stdout();
    tokio::spawn(handle_outgoing_requests(rx, stdout));

    loop {
        line.clear();
        match reader.read_line(&mut line).await {
            Ok(0) => {
                // EOF: orchestrator closed stdin
                safe_log!(info, "IPC stdin closed, terminating IPC loop");
                break;
            }
            Ok(_) => {
                let trimmed = line.trim_end();

                // Check if this is a response to a pending request
                if let Some(response_id) = extract_response_id(trimmed) {
                    let mut pending = IPC_CHANNEL.1.lock().await;
                    if let Some(sender) = pending.remove(&response_id) {
                        let _ = sender.send(trimmed.to_string());
                        continue;
                    }
                }

                // Otherwise, handle as a normal message
                match serde_json::from_str::<OrchestratorToModule>(trimmed) {
                    Ok(msg) => match msg {
                        OrchestratorToModule::Secret(_) | OrchestratorToModule::Rotated(_) => {
                            // Let SecretClient handle secret and rotation
                            if let Err(e) = client.process_server_message(trimmed).await {
                                safe_log!(error, "Error processing secret RPC: {}", e);
                            }
                        }
                        OrchestratorToModule::Shutdown => {
                            safe_log!(info, "Received shutdown command");
                            break;
                        }
                        OrchestratorToModule::Init(_) => {
                            // ignore extra init messages
                        }
                        OrchestratorToModule::ServiceResponse(_)
                        | OrchestratorToModule::ServiceOperationResult(_) => {
                            // These should have been handled by the response_id check
                            safe_log!(warn, "Service response not claimed: {}", trimmed);
                        }
                    },
                    Err(e) => {
                        safe_log!(error, "Failed to parse IPC message: {} raw: {}", e, trimmed);
                    }
                }
            }
            Err(e) => {
                safe_log!(error, "Error reading IPC stdin: {}", e);
                break;
            }
        }
    }

    // Shutdown IPC channel
    {
        let mut channel = IPC_CHANNEL.0.lock().await;
        *channel = None;
    }
}

// Extracts response ID from a message if it's a response to a service request
fn extract_response_id(message: &str) -> Option<String> {
    // Parse the message to check if it's a service response/result
    if let Ok(msg) = serde_json::from_str::<OrchestratorToModule>(message) {
        match msg {
            OrchestratorToModule::ServiceResponse(response) => {
                return Some(response.id);
            }
            OrchestratorToModule::ServiceOperationResult(_) => {
                // For operation results, use the connection_id as the response ID
                if let Some(op) = extract_original_operation(message) {
                    return Some(op.connection_id);
                }
            }
            _ => {}
        }
    }
    None
}

// Extract the original operation from a response message
fn extract_original_operation(message: &str) -> Option<ServiceOperation> {
    // This is a simplification as the real implementation would need to track
    // which operation a result is for. For now, we'll parse the message to get the operation.
    if let Ok(value) = serde_json::from_str::<serde_json::Value>(message) {
        if let Some(params) = value.get("params") {
            if let Ok(op) = serde_json::from_value::<ServiceOperation>(params.clone()) {
                return Some(op);
            }
        }
    }
    None
}

// Task to handle outgoing requests
async fn handle_outgoing_requests(
    mut rx: Receiver<(String, oneshot::Sender<String>)>,
    mut stdout: io::Stdout,
) {
    while let Some((request, response_sender)) = rx.recv().await {
        // Send request to orchestrator
        if let Err(e) = stdout.write_all(format!("{}\n", request).as_bytes()).await {
            safe_log!(error, "Error writing to stdout: {}", e);
            let _ = response_sender.send(format!("{{\"error\":\"IPC write error: {}\"}}", e));
            continue;
        }
        if let Err(e) = stdout.flush().await {
            safe_log!(error, "Error flushing stdout: {}", e);
            let _ = response_sender.send(format!("{{\"error\":\"IPC flush error: {}\"}}", e));
            continue;
        }

        // Request sent successfully, response will be received by the main loop
    }
}

/// Send a request to the orchestrator and wait for the response
pub async fn send_request<T>(request: &T) -> Result<String, String>
where
    T: serde::Serialize + std::fmt::Debug + Clone,
{
    // Create channel for receiving response
    let (sender, receiver) = oneshot::channel::<String>();

    // Get request ID for tracking the response
    let request_id = get_request_id(request)?;

    // Register response handler
    {
        let mut pending = IPC_CHANNEL.1.lock().await;
        pending.insert(request_id, sender);
    }

    // Convert to ModuleToOrchestrator and serialize
    let module_request = convert_to_module_request(request)?;
    let request_json = serde_json::to_string(&module_request)
        .map_err(|e| format!("Failed to serialize request: {}", e))?;

    // Send request via the IPC channel
    let tx = {
        let channel_guard = IPC_CHANNEL.0.lock().await;
        match &*channel_guard {
            Some(sender) => sender.clone(),
            None => return Err("IPC channel not initialized".to_string()),
        }
    };

    // Create a dummy sender for the channel protocol but we'll use our registered one
    let dummy_sender = oneshot::channel::<String>().0;

    // Send the request through the channel
    tx.send((request_json, dummy_sender))
        .await
        .map_err(|_| "Failed to send request".to_string())?;

    // Wait for response on the receiver
    receiver
        .await
        .map_err(|_| "Failed to receive response".to_string())
}

// Convert a request to ModuleToOrchestrator
fn convert_to_module_request<T>(request: &T) -> Result<ModuleToOrchestrator, String>
where
    T: serde::Serialize + std::fmt::Debug,
{
    // Try to convert to ServiceRequest
    if let Ok(req) = serde_json::to_value(request)
        .map_err(|e| format!("Failed to serialize request: {}", e))
        .and_then(|v| {
            serde_json::from_value::<ServiceRequest>(v)
                .map_err(|e| format!("Failed to parse as ServiceRequest: {}", e))
        })
    {
        return Ok(ModuleToOrchestrator::ServiceRequest(req));
    }

    // Try to convert to ServiceOperation
    if let Ok(op) = serde_json::to_value(request)
        .map_err(|e| format!("Failed to serialize request: {}", e))
        .and_then(|v| {
            serde_json::from_value::<ServiceOperation>(v)
                .map_err(|e| format!("Failed to parse as ServiceOperation: {}", e))
        })
    {
        return Ok(ModuleToOrchestrator::ServiceOperation(op));
    }

    // If direct conversion fails, fail with error message
    Err(format!(
        "Could not convert request to ModuleToOrchestrator: {:?}",
        request
    ))
}

/// Receive and parse a response from the orchestrator
pub fn receive_response<T>(response: String) -> Result<T, String>
where
    T: serde::de::DeserializeOwned,
{
    // Parse response
    serde_json::from_str::<T>(&response).map_err(|e| format!("Failed to parse response: {}", e))
}

// Get the request ID from a request
fn get_request_id<T>(request: &T) -> Result<String, String>
where
    T: serde::Serialize + std::fmt::Debug,
{
    if let Ok(service_req) = serde_json::to_value(request)
        .map_err(|e| format!("Failed to serialize request: {}", e))
        .and_then(|v| {
            serde_json::from_value::<ServiceRequest>(v)
                .map_err(|e| format!("Failed to parse as ServiceRequest: {}", e))
        })
    {
        return Ok(service_req.id);
    }

    if let Ok(service_op) = serde_json::to_value(request)
        .map_err(|e| format!("Failed to serialize request: {}", e))
        .and_then(|v| {
            serde_json::from_value::<ServiceOperation>(v)
                .map_err(|e| format!("Failed to parse as ServiceOperation: {}", e))
        })
    {
        return Ok(service_op.connection_id);
    }

    // For other request types, use a dummy ID
    Ok("general_request".to_string())
}
