use std::collections::HashMap;

use chrono::{DateTime, Utc};
use futures::{SinkExt, StreamExt};
use gloo_net::websocket::{futures::WebSocket, Message, WebSocketError};
use leptos::prelude::*;
use serde::{Deserialize, Serialize};
use wasm_bindgen_futures::spawn_local;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebSocketMessage {
    pub id: String,
    pub message_type: String,
    pub data: serde_json::Value,
    pub timestamp: DateTime<Utc>,
    pub source: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubscriptionRequest {
    pub action: String,
    pub topics: Vec<String>,
    pub filters: Option<HashMap<String, serde_json::Value>>,
}

#[derive(Debug, Clone)]
pub enum ConnectionState {
    Connecting,
    Connected,
    Disconnected,
    Error(String),
    Reconnecting,
}

#[derive(Debug, Clone)]
pub struct WebSocketService {
    pub connection_state: ReadSignal<ConnectionState>,
    pub message_stream: ReadSignal<Option<WebSocketMessage>>,
    pub error_stream: ReadSignal<Option<String>>,
    pub metrics: ReadSignal<WebSocketMetrics>,
}

#[derive(Debug, Clone, Default)]
pub struct WebSocketMetrics {
    pub messages_received: u32,
    pub messages_sent: u32,
    pub connection_attempts: u32,
    pub last_connection: Option<DateTime<Utc>>,
    pub uptime: Option<chrono::Duration>,
    pub reconnection_count: u32,
}

impl WebSocketService {
    pub fn new() -> Self {
        let (connection_state, set_connection_state) = signal(ConnectionState::Disconnected);
        let (message_stream, set_message_stream) = signal(None);
        let (error_stream, set_error_stream) = signal(None);
        let (metrics, set_metrics) = signal(WebSocketMetrics::default());

        Self {
            connection_state,
            message_stream,
            error_stream,
            metrics,
        }
    }

    pub fn connect(&self, url: &str) -> WebSocketConnection {
        let url = url.to_string();
        let (connection_state, set_connection_state) = signal(ConnectionState::Connecting);
        let (message_stream, set_message_stream) = signal(None);
        let (error_stream, set_error_stream) = signal(None);
        let (metrics, set_metrics) = signal(WebSocketMetrics::default());

        // Create WebSocket connection
        let connection = WebSocketConnection::new(
            url,
            set_connection_state,
            set_message_stream,
            set_error_stream,
            set_metrics,
        );

        connection.connect();

        WebSocketConnection {
            connection_state,
            message_stream,
            error_stream,
            metrics,
            ..connection
        }
    }
}

#[derive(Debug, Clone)]
pub struct WebSocketConnection {
    pub url: String,
    pub connection_state: ReadSignal<ConnectionState>,
    pub message_stream: ReadSignal<Option<WebSocketMessage>>,
    pub error_stream: ReadSignal<Option<String>>,
    pub metrics: ReadSignal<WebSocketMetrics>,
    set_connection_state: WriteSignal<ConnectionState>,
    set_message_stream: WriteSignal<Option<WebSocketMessage>>,
    set_error_stream: WriteSignal<Option<String>>,
    set_metrics: WriteSignal<WebSocketMetrics>,
    sender: Option<futures::channel::mpsc::UnboundedSender<Message>>,
}

impl WebSocketConnection {
    fn new(
        url: String,
        set_connection_state: WriteSignal<ConnectionState>,
        set_message_stream: WriteSignal<Option<WebSocketMessage>>,
        set_error_stream: WriteSignal<Option<String>>,
        set_metrics: WriteSignal<WebSocketMetrics>,
    ) -> Self {
        let (connection_state, _) = signal(ConnectionState::Disconnected);
        let (message_stream, _) = signal(None);
        let (error_stream, _) = signal(None);
        let (metrics, _) = signal(WebSocketMetrics::default());

        Self {
            url,
            connection_state,
            message_stream,
            error_stream,
            metrics,
            set_connection_state,
            set_message_stream,
            set_error_stream,
            set_metrics,
            sender: None,
        }
    }

    pub fn connect(&self) {
        let url = self.url.clone();
        let set_connection_state = self.set_connection_state;
        let set_message_stream = self.set_message_stream;
        let set_error_stream = self.set_error_stream;
        let set_metrics = self.set_metrics;

        spawn_local(async move {
            set_connection_state.set(ConnectionState::Connecting);

            // Update connection metrics
            set_metrics.update(|m| {
                m.connection_attempts += 1;
                m.last_connection = Some(Utc::now());
            });

            match WebSocket::open(&url) {
                Ok(ws) => {
                    set_connection_state.set(ConnectionState::Connected);
                    tracing::info!("WebSocket connected to {}", url);

                    let (mut sink, mut stream) = ws.split();
                    let (sender, mut receiver) = futures::channel::mpsc::unbounded();

                    // Handle outgoing messages
                    spawn_local(async move {
                        while let Some(msg) = receiver.next().await {
                            if let Err(e) = sink.send(msg).await {
                                tracing::error!("Failed to send WebSocket message: {:?}", e);
                                set_error_stream.set(Some(format!("Send error: {:?}", e)));
                                break;
                            }
                        }
                    });

                    // Handle incoming messages
                    let connection_start = Utc::now();
                    while let Some(msg) = stream.next().await {
                        match msg {
                            Ok(Message::Text(text)) => {
                                // Update metrics
                                set_metrics.update(|m| {
                                    m.messages_received += 1;
                                    m.uptime = Some(Utc::now() - connection_start);
                                });

                                // Parse and forward message
                                match serde_json::from_str::<WebSocketMessage>(&text) {
                                    Ok(ws_message) => {
                                        set_message_stream.set(Some(ws_message));
                                    }
                                    Err(e) => {
                                        tracing::warn!("Failed to parse WebSocket message: {}", e);
                                        // Create a generic message for unparseable content
                                        let generic_message = WebSocketMessage {
                                            id: uuid::Uuid::new_v4().to_string(),
                                            message_type: "raw_text".to_string(),
                                            data: serde_json::Value::String(text),
                                            timestamp: Utc::now(),
                                            source: Some("websocket".to_string()),
                                        };
                                        set_message_stream.set(Some(generic_message));
                                    }
                                }
                            }
                            Ok(Message::Bytes(bytes)) => {
                                tracing::debug!(
                                    "Received binary WebSocket message: {} bytes",
                                    bytes.len()
                                );
                                // Handle binary messages if needed
                                let generic_message = WebSocketMessage {
                                    id: uuid::Uuid::new_v4().to_string(),
                                    message_type: "binary_data".to_string(),
                                    data: serde_json::Value::String(format!(
                                        "Binary data: {} bytes",
                                        bytes.len()
                                    )),
                                    timestamp: Utc::now(),
                                    source: Some("websocket".to_string()),
                                };
                                set_message_stream.set(Some(generic_message));
                            }
                            Err(e) => {
                                tracing::error!("WebSocket error: {:?}", e);
                                set_error_stream.set(Some(format!("WebSocket error: {:?}", e)));
                                set_connection_state
                                    .set(ConnectionState::Error(format!("{:?}", e)));
                                break;
                            }
                        }
                    }

                    // Connection closed
                    set_connection_state.set(ConnectionState::Disconnected);
                    tracing::info!("WebSocket disconnected from {}", url);
                }
                Err(e) => {
                    let error_msg = format!("Failed to connect to WebSocket: {:?}", e);
                    tracing::error!("{}", error_msg);
                    set_connection_state.set(ConnectionState::Error(error_msg.clone()));
                    set_error_stream.set(Some(error_msg));
                }
            }
        });
    }

    pub fn send_message(&mut self, message: WebSocketMessage) -> Result<(), String> {
        if let Some(ref sender) = self.sender {
            let json_str = serde_json::to_string(&message)
                .map_err(|e| format!("Failed to serialize message: {}", e))?;

            sender
                .unbounded_send(Message::Text(json_str))
                .map_err(|e| format!("Failed to send message: {}", e))?;

            // Update metrics
            self.set_metrics.update(|m| m.messages_sent += 1);

            Ok(())
        } else {
            Err("WebSocket not connected".to_string())
        }
    }

    pub fn send_subscription_request(
        &mut self,
        request: SubscriptionRequest,
    ) -> Result<(), String> {
        let message = WebSocketMessage {
            id: uuid::Uuid::new_v4().to_string(),
            message_type: "subscription".to_string(),
            data: serde_json::to_value(request)
                .map_err(|e| format!("Failed to serialize subscription: {}", e))?,
            timestamp: Utc::now(),
            source: Some("dashboard".to_string()),
        };

        self.send_message(message)
    }

    pub fn subscribe_to_metrics(&mut self) -> Result<(), String> {
        let subscription = SubscriptionRequest {
            action: "subscribe".to_string(),
            topics: vec![
                "workflow_metrics".to_string(),
                "system_metrics".to_string(),
                "security_events".to_string(),
                "policy_violations".to_string(),
                "user_activities".to_string(),
                "system_health".to_string(),
            ],
            filters: None,
        };

        self.send_subscription_request(subscription)
    }

    pub fn subscribe_to_topic(
        &mut self,
        topic: &str,
        filters: Option<HashMap<String, serde_json::Value>>,
    ) -> Result<(), String> {
        let subscription = SubscriptionRequest {
            action: "subscribe".to_string(),
            topics: vec![topic.to_string()],
            filters,
        };

        self.send_subscription_request(subscription)
    }

    pub fn unsubscribe_from_topic(&mut self, topic: &str) -> Result<(), String> {
        let subscription = SubscriptionRequest {
            action: "unsubscribe".to_string(),
            topics: vec![topic.to_string()],
            filters: None,
        };

        self.send_subscription_request(subscription)
    }

    pub fn reconnect(&self) {
        let set_connection_state = self.set_connection_state;
        let set_metrics = self.set_metrics;

        set_connection_state.set(ConnectionState::Reconnecting);
        set_metrics.update(|m| m.reconnection_count += 1);

        // Schedule reconnection with a delay
        let _self_url = self.url.clone();
        spawn_local(async move {
            // Simple async delay using yield
            std::mem::drop(futures::future::pending::<()>());
        });

        // Note: In a full implementation, this should be delayed
        // For now, reconnection happens immediately
        self.connect();
    }

    pub fn disconnect(&self) {
        self.set_connection_state.set(ConnectionState::Disconnected);
        // Close WebSocket connection
        // This would typically involve closing the underlying WebSocket
    }

    pub fn is_connected(&self) -> bool {
        matches!(self.connection_state.get(), ConnectionState::Connected)
    }

    pub fn get_connection_info(&self) -> ConnectionInfo {
        let metrics = self.metrics.get();
        let state = self.connection_state.get();

        ConnectionInfo {
            url: self.url.clone(),
            state,
            metrics,
            is_healthy: self.is_healthy(),
        }
    }

    fn is_healthy(&self) -> bool {
        match self.connection_state.get() {
            ConnectionState::Connected => {
                let metrics = self.metrics.get();
                // Consider connection healthy if we've received messages recently
                // and have no recent errors
                metrics.messages_received > 0
            }
            _ => false,
        }
    }
}

#[derive(Debug, Clone)]
pub struct ConnectionInfo {
    pub url: String,
    pub state: ConnectionState,
    pub metrics: WebSocketMetrics,
    pub is_healthy: bool,
}

// Dashboard-specific WebSocket hook
#[derive(Clone)]
pub struct DashboardWebSocket {
    connection: WebSocketConnection,
}

impl DashboardWebSocket {
    pub fn new(url: &str) -> Self {
        let service = WebSocketService::new();
        let connection = service.connect(url);

        Self { connection }
    }

    #[allow(clippy::type_complexity)]
    pub fn use_dashboard_websocket(
        url: &str,
    ) -> (
        ReadSignal<ConnectionState>,
        ReadSignal<Option<WebSocketMessage>>,
        ReadSignal<Option<String>>,
        Box<dyn Fn(&str) -> Result<(), String> + Send + Sync>,
        Box<dyn Fn() + Send + Sync>,
    ) {
        let mut ws = Self::new(url);

        // Auto-subscribe to dashboard metrics
        let _ = ws.connection.subscribe_to_metrics();

        let connection_state = ws.connection.connection_state;
        let message_stream = ws.connection.message_stream;
        let error_stream = ws.connection.error_stream;

        let send_message = Box::new(move |msg: &str| -> Result<(), String> {
            // Implementation for sending custom messages
            Ok(())
        });

        let reconnect = Box::new(move || {
            ws.connection.reconnect();
        });

        (
            connection_state,
            message_stream,
            error_stream,
            send_message,
            reconnect,
        )
    }
}

// Auto-reconnection with exponential backoff
pub struct ReconnectionManager {
    max_attempts: u32,
    base_delay: u32, // milliseconds
    max_delay: u32,  // milliseconds
    current_attempt: u32,
}

impl Default for ReconnectionManager {
    fn default() -> Self {
        Self {
            max_attempts: 10,
            base_delay: 1000, // 1 second
            max_delay: 30000, // 30 seconds
            current_attempt: 0,
        }
    }
}

impl ReconnectionManager {
    pub fn should_reconnect(&self) -> bool {
        self.current_attempt < self.max_attempts
    }

    pub fn get_delay(&self) -> u32 {
        let delay = self.base_delay * 2_u32.pow(self.current_attempt);
        delay.min(self.max_delay)
    }

    pub fn attempt_reconnection(&mut self) {
        self.current_attempt += 1;
    }

    pub fn reset(&mut self) {
        self.current_attempt = 0;
    }
}
