use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tauri::{AppHandle, Emitter};
use tracing::{Level, Subscriber};
use tracing_subscriber::layer::{Context, Layer};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DebugLogEvent {
    pub level: String,
    pub message: String,
}

#[derive(Clone)]
pub struct TauriLogLayer {
    app: Arc<AppHandle>,
}

impl TauriLogLayer {
    pub fn new(app: AppHandle) -> Self {
        Self {
            app: Arc::new(app),
        }
    }
}

impl<S> Layer<S> for TauriLogLayer
where
    S: Subscriber,
{
    fn on_event(
        &self,
        event: &tracing::Event<'_>,
        _ctx: Context<'_, S>,
    ) {
        let metadata = event.metadata();
        let level = match *metadata.level() {
            Level::ERROR => "error",
            Level::WARN => "warn",
            Level::INFO => "info",
            Level::DEBUG => "debug",
            Level::TRACE => "debug",
        };

        // Extract message from the event
        struct MessageVisitor {
            message: String,
        }

        impl tracing::field::Visit for MessageVisitor {
            fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
                if field.name() == "message" {
                    self.message = format!("{:?}", value);
                }
            }
        }

        let mut visitor = MessageVisitor {
            message: String::new(),
        };
        event.record(&mut visitor);

        // If we couldn't extract a message, try to format the whole event
        if visitor.message.is_empty() {
            visitor.message = metadata.name().to_string();
        }

        // Strip quotes from the message
        let message = visitor.message.trim_matches('"').to_string();

        let log_event = DebugLogEvent {
            level: level.to_string(),
            message,
        };

        // Emit to frontend
        let _ = self.app.emit("debug:log", log_event);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_debug_log_event_creation() {
        let event = DebugLogEvent {
            level: "info".to_string(),
            message: "Test message".to_string(),
        };

        assert_eq!(event.level, "info");
        assert_eq!(event.message, "Test message");
    }

    #[test]
    fn test_debug_log_event_serialization() {
        let event = DebugLogEvent {
            level: "error".to_string(),
            message: "Error occurred".to_string(),
        };

        let json = serde_json::to_string(&event).unwrap();
        assert!(json.contains("\"level\":\"error\""));
        assert!(json.contains("\"message\":\"Error occurred\""));
    }

    #[test]
    fn test_debug_log_event_deserialization() {
        let json = r#"{"level":"warn","message":"Warning message"}"#;
        let event: DebugLogEvent = serde_json::from_str(json).unwrap();

        assert_eq!(event.level, "warn");
        assert_eq!(event.message, "Warning message");
    }

    #[test]
    fn test_debug_log_event_with_special_characters() {
        let event = DebugLogEvent {
            level: "info".to_string(),
            message: "Message with \"quotes\" and \n newlines".to_string(),
        };

        let json = serde_json::to_string(&event).unwrap();
        let decoded: DebugLogEvent = serde_json::from_str(&json).unwrap();

        assert_eq!(decoded.level, event.level);
        assert_eq!(decoded.message, event.message);
    }

    #[test]
    fn test_debug_log_event_with_unicode() {
        let event = DebugLogEvent {
            level: "debug".to_string(),
            message: "Unicode: 日本語 🎉".to_string(),
        };

        let json = serde_json::to_string(&event).unwrap();
        let decoded: DebugLogEvent = serde_json::from_str(&json).unwrap();

        assert_eq!(decoded.message, "Unicode: 日本語 🎉");
    }

    #[test]
    fn test_debug_log_event_all_levels() {
        let levels = vec!["error", "warn", "info", "debug", "trace"];

        for level in levels {
            let event = DebugLogEvent {
                level: level.to_string(),
                message: format!("{} level message", level),
            };

            assert_eq!(event.level, level);
            assert!(event.message.contains(level));
        }
    }
}
