use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use ucxl_core::*;

// Helper functions for creating standard responses
fn create_error_response(code: UCXLErrorCode, path: &str, request_id: Option<&str>) -> CoreToUiResponse {
    let error = UCXLErrorBuilder::new(code)
        .source("ucxl-browser")
        .path(path)
        .request_id(request_id.unwrap_or("unknown"))
        .build();
    CoreToUiResponse::Error(error)
}

fn create_success_response<T>(code: UCXLResponseCode, data: Option<T>, request_id: Option<&str>) -> CoreToUiResponse 
where T: serde::Serialize
{
    let response = UCXLResponseBuilder::new(code)
        .data(serde_json::to_value(data).unwrap_or(serde_json::Value::Null))
        .request_id(request_id.unwrap_or("unknown"))
        .build();
    CoreToUiResponse::Success(response)
}

fn create_envelope_error(code: UCXLErrorCode, envelope_id: &str, path: &str) -> CoreToUiResponse {
    let error = UCXLErrorBuilder::new(code)
        .field("envelope_id", envelope_id.into())
        .source("ucxl-browser")
        .path(path)
        .build();
    CoreToUiResponse::Error(error)
}

fn create_uri_error(uri: &str, path: &str) -> CoreToUiResponse {
    let error = UCXLErrorBuilder::new(UCXLErrorCode::InvalidAddress)
        .field("address", uri.into())
        .expected_format("ucxl://<agent>:<role>@<project>:<task>[/<temporal>/<path>]")
        .source("ucxl-browser")
        .path(path)
        .cause("parse_error")
        .build();
    CoreToUiResponse::Error(error)
}

// Global state for the application
#[derive(Clone)]
pub struct AppState {
    pub store: Arc<Mutex<InMemoryEnvelopeStore>>,
    pub dht_store: Arc<Mutex<Option<DHTEnvelopeStore>>>,
    pub bzzz_gateway: Arc<Mutex<Option<BZZZGateway>>>,
    pub temporal_engine: Arc<Mutex<Option<TemporalEngine<InMemoryEnvelopeStore>>>>,
    pub command_executor: Arc<Mutex<UCXLCommandExecutor>>,
}

impl AppState {
    pub fn new() -> Self {
        let store = InMemoryEnvelopeStore::new();
        
        // Initialize BZZZ Gateway with DHT
        let bzzz_config = BZZZConfig::default();
        let bzzz_gateway = BZZZGateway::new(bzzz_config).unwrap();
        
        AppState {
            store: Arc::new(Mutex::new(store)),
            dht_store: Arc::new(Mutex::new(None)),
            bzzz_gateway: Arc::new(Mutex::new(Some(bzzz_gateway))),
            temporal_engine: Arc::new(Mutex::new(None)),
            command_executor: Arc::new(Mutex::new(UCXLCommandExecutor::new())),
        }
    }
    
    pub async fn initialize_dht(&self) -> std::result::Result<(), String> {
        // Start BZZZ Gateway
        let mut gateway_clone = {
            let gateway_opt = self.bzzz_gateway.lock().map_err(|e| e.to_string())?;
            gateway_opt.clone()
        };
        
        if let Some(ref mut gateway) = gateway_clone.as_mut() {
            gateway.start().await.map_err(|e| e.to_string())?;
            
            // Update the state with the started gateway
            if let Ok(mut gateway_opt) = self.bzzz_gateway.lock() {
                *gateway_opt = gateway_clone;
            }
        }
        
        Ok(())
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum UiToCoreCommand {
    // Basic envelope operations
    GetEnvelope { envelope_id: String },
    GetEnvelopeByUri { uri: String },
    StoreEnvelope { envelope: EnvelopeData },
    DeleteEnvelope { envelope_id: String, soft_delete: bool },
    
    // UCXL protocol commands
    ExecuteUCXLGet { uri: String, version: Option<String>, at_time: Option<String> },
    ExecuteUCXLPut { uri: String, content: String, content_type: String, metadata: HashMap<String, serde_json::Value> },
    ExecuteUCXLPost { uri: String, markdown_content: String, author: Option<String>, title: Option<String> },
    ExecuteUCXLAnnounce { uri: String, envelope_id: String, announcement_data: HashMap<String, serde_json::Value> },
    ExecuteUCXLDelete { uri: String, version: Option<String>, soft_delete: bool },
    
    // Temporal navigation commands
    GetTemporalState { doc_id: String, version_id: Option<String>, at_time: Option<String>, branch_name: Option<String> },
    GetTimeline { doc_id: String },
    DiffVersions { doc_id: String, from_version: String, to_version: String },
    
    // Search commands
    SearchEnvelopes { query: SearchQueryData },
    
    // Store statistics
    GetStoreStats,
    
    // DHT and BZZZ operations
    InitializeDHT,
    GetNetworkStatus,
    GetBZZZStats,
    StoreToDHT { envelope: EnvelopeData },
    RetrieveFromDHT { envelope_id: String },
    GetPeerList,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EnvelopeData {
    pub ucxl_uri: String,
    pub content: String,
    pub content_type: String,
    pub author: Option<String>,
    pub title: Option<String>,
    pub tags: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SearchQueryData {
    pub text: Option<String>,
    pub tags: Vec<String>,
    pub author: Option<String>,
    pub content_type: Option<String>,
    pub limit: Option<usize>,
    pub offset: Option<usize>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum CoreToUiResponse {
    // Standardized UCXL responses
    Success(UCXLSuccessResponse),
    Error(UCXLErrorResponse),
    
    // Legacy responses for backward compatibility
    Envelope(Envelope),
    EnvelopeList(Vec<Envelope>),
    
    // UCXL command responses
    UCXLResponse(UCXLResponse),
    
    // Temporal responses
    TemporalState(TemporalState),
    Timeline(TimelineView),
    VersionDiff(VersionDiff),
    
    // Store info
    StoreStats(StoreStats),
    
    // DHT and BZZZ responses
    NetworkStatus(NetworkStatus),
    BZZZStats(BZZZStats),
    PeerList(Vec<BZZZPeer>),
}

// Tauri command handlers

#[tauri::command]
pub async fn handle_command(command: UiToCoreCommand, state: tauri::State<'_, AppState>) -> std::result::Result<CoreToUiResponse, String> {
    match command {
        UiToCoreCommand::GetEnvelope { envelope_id } => {
            // Clone the store for async usage
            let result = {
                let store = match state.store.lock() {
                    Ok(store) => store.clone(),
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire store lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                store.retrieve(&envelope_id).await
            };
            
            match result {
                Ok(Some(envelope)) => Ok(CoreToUiResponse::Envelope(envelope)),
                Ok(None) => Ok(CoreToUiResponse::Error { 
                    error: "Envelope not found".to_string(),
                    details: Some(envelope_id),
                }),
                Err(e) => Ok(CoreToUiResponse::Error { 
                    error: e.to_string(),
                    details: None,
                }),
            }
        }
        
        UiToCoreCommand::GetEnvelopeByUri { uri } => {
            let ucxl_uri = match UCXLUri::new(&uri) {
                Ok(uri) => uri,
                Err(_) => return Ok(create_uri_error(&uri, "/envelope/by-uri")),
            };
            
            let result = {
                let store = match state.store.lock() {
                    Ok(store) => store.clone(),
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire store lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                store.retrieve_by_uri(&ucxl_uri).await
            };
            
            match result {
                Ok(Some(envelope)) => Ok(CoreToUiResponse::Envelope(envelope)),
                Ok(None) => Ok(create_envelope_error(UCXLErrorCode::NotFound, &uri, "/envelope/by-uri")),
                Err(e) => Ok(create_error_response(
                    UCXLErrorCode::InternalError,
                    "/envelope/by-uri",
                    None
                )),
            }
        }
        
        UiToCoreCommand::StoreEnvelope { envelope } => {
            let ucxl_uri = match UCXLUri::new(&envelope.ucxl_uri) {
                Ok(uri) => uri,
                Err(e) => return Ok(CoreToUiResponse::Error {
                    error: "Invalid UCXL URI".to_string(),
                    details: Some(e.to_string()),
                }),
            };
            
            let metadata = EnvelopeMetadata {
                author: envelope.author,
                title: envelope.title,
                tags: envelope.tags,
                source: Some("ucxl-browser".to_string()),
                context_data: HashMap::new(),
            };
            
            let new_envelope = match Envelope::new(ucxl_uri, envelope.content, envelope.content_type, metadata) {
                Ok(env) => env,
                Err(e) => return Ok(CoreToUiResponse::Error {
                    error: "Failed to create envelope".to_string(),
                    details: Some(e.to_string()),
                }),
            };
            
            let envelope_id = new_envelope.id.clone();
            let result = {
                let store = match state.store.lock() {
                    Ok(store) => store.clone(),
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire store lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                store.store(&new_envelope).await
            };
                
            match result {
                Ok(()) => Ok(CoreToUiResponse::Success { 
                    message: "Envelope stored successfully".to_string(),
                    data: Some(serde_json::json!({ "envelope_id": envelope_id })),
                }),
                Err(e) => Ok(CoreToUiResponse::Error { 
                    error: e.to_string(),
                    details: None,
                }),
            }
        }
        
        UiToCoreCommand::ExecuteUCXLGet { uri, version, at_time } => {
            let ucxl_uri = match UCXLUri::new(&uri) {
                Ok(uri) => uri,
                Err(e) => return Ok(CoreToUiResponse::Error {
                    error: "Invalid UCXL URI".to_string(),
                    details: Some(e.to_string()),
                }),
            };
            
            let parsed_at_time = if let Some(at_time_str) = at_time {
                match chrono::DateTime::parse_from_rfc3339(&at_time_str) {
                    Ok(dt) => Some(dt.with_timezone(&chrono::Utc)),
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Invalid date format".to_string(),
                        details: Some(e.to_string()),
                    }),
                }
            } else {
                None
            };
            
            let get_cmd = GetCommand {
                ucxl_uri,
                version,
                at_time: parsed_at_time,
            };
            
            let result = {
                let executor = match state.command_executor.lock() {
                    Ok(exec) => exec.clone(),
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire executor lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                executor.execute(UCXLCommand::Get(get_cmd)).await
            };
            
            match result {
                Ok(response) => Ok(CoreToUiResponse::UCXLResponse(response)),
                Err(e) => Ok(CoreToUiResponse::Error { 
                    error: e.to_string(),
                    details: None,
                }),
            }
        }
        
        UiToCoreCommand::ExecuteUCXLPost { uri, markdown_content, author, title } => {
            let ucxl_uri = match UCXLUri::new(&uri) {
                Ok(uri) => uri,
                Err(e) => return Ok(CoreToUiResponse::Error {
                    error: "Invalid UCXL URI".to_string(),
                    details: Some(e.to_string()),
                }),
            };
            
            let doc_id = ucxl_uri.path.clone();
            let markdown_context = MarkdownContext::new(doc_id, markdown_content, author, title);
            
            let post_cmd = PostCommand {
                ucxl_uri,
                markdown_context,
            };
            
            let result = {
                let executor = match state.command_executor.lock() {
                    Ok(exec) => exec.clone(),
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire executor lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                executor.execute(UCXLCommand::Post(post_cmd)).await
            };
            
            match result {
                Ok(response) => Ok(CoreToUiResponse::UCXLResponse(response)),
                Err(e) => Ok(CoreToUiResponse::Error { 
                    error: e.to_string(),
                    details: None,
                }),
            }
        }
        
        UiToCoreCommand::SearchEnvelopes { query } => {
            let search_query = SearchQuery {
                text: query.text,
                tags: query.tags,
                author: query.author,
                content_type: query.content_type,
                date_range: None, // TODO: Add date range support in UI
                limit: query.limit,
                offset: query.offset,
            };
            
            let result = {
                let store = match state.store.lock() {
                    Ok(store) => store.clone(),
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire store lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                store.search(&search_query).await
            };
            
            match result {
                Ok(envelopes) => Ok(CoreToUiResponse::EnvelopeList(envelopes)),
                Err(e) => Ok(CoreToUiResponse::Error { 
                    error: e.to_string(),
                    details: None,
                }),
            }
        }
        
        UiToCoreCommand::GetStoreStats => {
            let stats = {
                let store = match state.store.lock() {
                    Ok(store) => store,
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire store lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                store.get_stats()
            };
            Ok(CoreToUiResponse::StoreStats(stats))
        }
        
        UiToCoreCommand::InitializeDHT => {
            match state.initialize_dht().await {
                Ok(()) => Ok(CoreToUiResponse::Success {
                    message: "DHT network initialized successfully".to_string(),
                    data: None,
                }),
                Err(e) => Ok(CoreToUiResponse::Error {
                    error: "Failed to initialize DHT".to_string(),
                    details: Some(e),
                }),
            }
        }
        
        UiToCoreCommand::GetNetworkStatus => {
            let gateway_clone = {
                let gateway_opt = match state.bzzz_gateway.lock() {
                    Ok(gateway) => gateway,
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire gateway lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                
                gateway_opt.clone()
            };
            
            let network_status = if let Some(gateway) = gateway_clone {
                gateway.get_network_status().await
            } else {
                return Ok(CoreToUiResponse::Error {
                    error: "DHT network not initialized".to_string(),
                    details: Some("Call InitializeDHT first".to_string()),
                });
            };
            
            Ok(CoreToUiResponse::NetworkStatus(network_status))
        }
        
        UiToCoreCommand::GetBZZZStats => {
            let gateway_clone = {
                let gateway_opt = match state.bzzz_gateway.lock() {
                    Ok(gateway) => gateway,
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire gateway lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                
                gateway_opt.clone()
            };
            
            let bzzz_stats = if let Some(gateway) = gateway_clone {
                gateway.get_gateway_stats()
            } else {
                return Ok(create_error_response(
                    UCXLErrorCode::ServiceUnavailable,
                    "/bzzz/stats",
                    None
                ));
            };
            
            Ok(CoreToUiResponse::BZZZStats(bzzz_stats))
        }
        
        UiToCoreCommand::StoreToDHT { envelope } => {
            let ucxl_uri = match UCXLUri::new(&envelope.ucxl_uri) {
                Ok(uri) => uri,
                Err(_) => return Ok(create_uri_error(&envelope.ucxl_uri, "/dht/store")),
            };
            
            let metadata = EnvelopeMetadata {
                author: envelope.author,
                title: envelope.title,
                tags: envelope.tags,
                source: Some("ucxl-browser".to_string()),
                context_data: HashMap::new(),
            };
            
            let new_envelope = match Envelope::new(ucxl_uri, envelope.content, envelope.content_type, metadata) {
                Ok(env) => env,
                Err(e) => return Ok(CoreToUiResponse::Error {
                    error: "Failed to create envelope".to_string(),
                    details: Some(e.to_string()),
                }),
            };
            
            let gateway_clone = {
                let gateway_opt = match state.bzzz_gateway.lock() {
                    Ok(gateway) => gateway,
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire gateway lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                
                gateway_opt.clone()
            };
            
            let envelope_id = if let Some(gateway) = gateway_clone {
                match gateway.store_context(&new_envelope).await {
                    Ok(id) => id,
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to store to DHT".to_string(),
                        details: Some(e.to_string()),
                    }),
                }
            } else {
                return Ok(CoreToUiResponse::Error {
                    error: "DHT network not initialized".to_string(),
                    details: Some("Call InitializeDHT first".to_string()),
                });
            };
            
            Ok(CoreToUiResponse::Success {
                message: "Content stored to DHT successfully".to_string(),
                data: Some(serde_json::json!({ "envelope_id": envelope_id })),
            })
        }
        
        UiToCoreCommand::RetrieveFromDHT { envelope_id } => {
            let gateway_clone = {
                let gateway_opt = match state.bzzz_gateway.lock() {
                    Ok(gateway) => gateway,
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to acquire gateway lock".to_string(),
                        details: Some(e.to_string()),
                    }),
                };
                
                gateway_opt.clone()
            };
            
            let envelope = if let Some(gateway) = gateway_clone {
                match gateway.retrieve_context(&envelope_id).await {
                    Ok(Some(env)) => env,
                    Ok(None) => return Ok(CoreToUiResponse::Error {
                        error: "Envelope not found in DHT".to_string(),
                        details: Some(envelope_id),
                    }),
                    Err(e) => return Ok(CoreToUiResponse::Error {
                        error: "Failed to retrieve from DHT".to_string(),
                        details: Some(e.to_string()),
                    }),
                }
            } else {
                return Ok(CoreToUiResponse::Error {
                    error: "DHT network not initialized".to_string(),
                    details: Some("Call InitializeDHT first".to_string()),
                });
            };
            
            Ok(CoreToUiResponse::Envelope(envelope))
        }
        
        UiToCoreCommand::GetPeerList => {
            // Mock peer list for now
            let peers = vec![
                BZZZPeer {
                    peer_id: "bzzz-peer-1".to_string(),
                    ip_address: "192.168.1.100".to_string(),
                    port: 8080,
                    last_seen: chrono::Utc::now(),
                    capabilities: PeerCapabilities {
                        supports_ucxl: true,
                        supports_dht: true,
                        supports_temporal: true,
                        storage_capacity: 1_000_000_000,
                        api_version: "v2.0".to_string(),
                    },
                    health_score: 0.95,
                    latency_ms: Some(15),
                },
                BZZZPeer {
                    peer_id: "bzzz-peer-2".to_string(),
                    ip_address: "192.168.1.101".to_string(),
                    port: 8080,
                    last_seen: chrono::Utc::now(),
                    capabilities: PeerCapabilities {
                        supports_ucxl: true,
                        supports_dht: true,
                        supports_temporal: false,
                        storage_capacity: 500_000_000,
                        api_version: "v2.0".to_string(),
                    },
                    health_score: 0.88,
                    latency_ms: Some(22),
                },
            ];
            
            Ok(CoreToUiResponse::PeerList(peers))
        }
        
        // TODO: Implement remaining commands
        _ => Ok(CoreToUiResponse::Error { 
            error: "Command not implemented yet".to_string(),
            details: Some(format!("{:?}", command)),
        }),
    }
}
