use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use crate::{Envelope, MarkdownContext, UCXLUri, Result};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum UCXLCommand {
    Get(GetCommand),
    Put(PutCommand),
    Post(PostCommand),
    Announce(AnnounceCommand),
    Delete(DeleteCommand),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct GetCommand {
    pub ucxl_uri: UCXLUri,
    pub version: Option<String>,
    pub at_time: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PutCommand {
    pub ucxl_uri: UCXLUri,
    pub content: String,
    pub content_type: String,
    pub metadata: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PostCommand {
    pub ucxl_uri: UCXLUri,
    pub markdown_context: MarkdownContext,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AnnounceCommand {
    pub ucxl_uri: UCXLUri,
    pub envelope_id: String,
    pub announcement_data: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DeleteCommand {
    pub ucxl_uri: UCXLUri,
    pub version: Option<String>,
    pub soft_delete: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum UCXLResponse {
    Envelope(Envelope),
    EnvelopeList(Vec<Envelope>),
    Success(SuccessResponse),
    Error(ErrorResponse),
    TemporalState(TemporalStateResponse),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SuccessResponse {
    pub message: String,
    pub envelope_id: Option<String>,
    pub version: Option<String>,
    pub metadata: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ErrorResponse {
    pub error_code: String,
    pub message: String,
    pub details: Option<HashMap<String, serde_json::Value>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TemporalStateResponse {
    pub envelope: Envelope,
    pub version_info: VersionState,
    pub is_reconstructed: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VersionState {
    pub current_version: String,
    pub parent_versions: Vec<String>,
    pub child_versions: Vec<String>,
    pub branch_info: Option<BranchInfo>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BranchInfo {
    pub branch_name: String,
    pub is_main_branch: bool,
    pub merge_base: Option<String>,
}

#[derive(Clone)]
pub struct UCXLCommandExecutor {
    // Will be implemented with actual storage and networking
}

impl UCXLCommandExecutor {
    pub fn new() -> Self {
        UCXLCommandExecutor {}
    }
    
    pub async fn execute(&self, command: UCXLCommand) -> Result<UCXLResponse> {
        match command {
            UCXLCommand::Get(cmd) => self.execute_get(cmd).await,
            UCXLCommand::Put(cmd) => self.execute_put(cmd).await,
            UCXLCommand::Post(cmd) => self.execute_post(cmd).await,
            UCXLCommand::Announce(cmd) => self.execute_announce(cmd).await,
            UCXLCommand::Delete(cmd) => self.execute_delete(cmd).await,
        }
    }
    
    async fn execute_get(&self, cmd: GetCommand) -> Result<UCXLResponse> {
        // TODO: Implement actual DHT retrieval
        // For now, return a mock envelope
        let mock_envelope = self.create_mock_envelope(&cmd.ucxl_uri)?;
        Ok(UCXLResponse::Envelope(mock_envelope))
    }
    
    async fn execute_put(&self, cmd: PutCommand) -> Result<UCXLResponse> {
        // TODO: Implement actual DHT storage
        // For now, return success
        Ok(UCXLResponse::Success(SuccessResponse {
            message: "Content stored successfully".to_string(),
            envelope_id: Some(cmd.ucxl_uri.to_content_hash()),
            version: Some(uuid::Uuid::new_v4().to_string()),
            metadata: cmd.metadata,
        }))
    }
    
    async fn execute_post(&self, cmd: PostCommand) -> Result<UCXLResponse> {
        // TODO: Implement markdown context submission to DHT
        let envelope = cmd.markdown_context.to_envelope(cmd.ucxl_uri)?;
        
        Ok(UCXLResponse::Success(SuccessResponse {
            message: "Markdown context submitted successfully".to_string(),
            envelope_id: Some(envelope.id),
            version: Some(envelope.version),
            metadata: HashMap::new(),
        }))
    }
    
    async fn execute_announce(&self, _cmd: AnnounceCommand) -> Result<UCXLResponse> {
        // TODO: Implement announcement to DHT network
        Ok(UCXLResponse::Success(SuccessResponse {
            message: "Envelope announced successfully".to_string(),
            envelope_id: None,
            version: None,
            metadata: HashMap::new(),
        }))
    }
    
    async fn execute_delete(&self, cmd: DeleteCommand) -> Result<UCXLResponse> {
        // TODO: Implement deletion (soft or hard)
        let message = if cmd.soft_delete {
            "Envelope soft deleted successfully"
        } else {
            "Envelope deleted successfully"
        };
        
        Ok(UCXLResponse::Success(SuccessResponse {
            message: message.to_string(),
            envelope_id: Some(cmd.ucxl_uri.to_content_hash()),
            version: cmd.version,
            metadata: HashMap::new(),
        }))
    }
    
    fn create_mock_envelope(&self, uri: &UCXLUri) -> Result<Envelope> {
        use crate::envelope::EnvelopeMetadata;
        
        let metadata = EnvelopeMetadata {
            author: Some("mock_author".to_string()),
            title: Some("Mock Document".to_string()),
            tags: vec!["mock".to_string(), "test".to_string()],
            source: Some("ucxl-browser".to_string()),
            context_data: HashMap::new(),
        };
        
        Envelope::new(
            uri.clone(),
            "# Mock Content\n\nThis is mock content for testing.".to_string(),
            "text/markdown".to_string(),
            metadata,
        )
    }
}

impl Default for UCXLCommandExecutor {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::UCXLUri;
    use tokio::runtime::Runtime;
    
    #[test]
    fn test_command_executor_get() {
        let rt = Runtime::new().unwrap();
        let executor = UCXLCommandExecutor::new();
        let uri = UCXLUri::new("ucxl://example.com/test").unwrap();
        
        let cmd = GetCommand {
            ucxl_uri: uri,
            version: None,
            at_time: None,
        };
        
        let response = rt.block_on(executor.execute_get(cmd)).unwrap();
        
        match response {
            UCXLResponse::Envelope(envelope) => {
                assert!(envelope.is_markdown());
                assert_eq!(envelope.metadata.author, Some("mock_author".to_string()));
            }
            _ => panic!("Expected Envelope response"),
        }
    }
    
    #[test]
    fn test_command_executor_put() {
        let rt = Runtime::new().unwrap();
        let executor = UCXLCommandExecutor::new();
        let uri = UCXLUri::new("ucxl://example.com/test").unwrap();
        
        let cmd = PutCommand {
            ucxl_uri: uri,
            content: "Test content".to_string(),
            content_type: "text/plain".to_string(),
            metadata: HashMap::new(),
        };
        
        let response = rt.block_on(executor.execute_put(cmd)).unwrap();
        
        match response {
            UCXLResponse::Success(success) => {
                assert!(success.envelope_id.is_some());
                assert!(success.version.is_some());
            }
            _ => panic!("Expected Success response"),
        }
    }
}