use crate::events::events::Emitter;
use crate::events::{BusReaderIterator, Event};
use crate::parsers::wasot::{Id, MetaLine, ParsedArguments, QuotedString};
use bus::Bus;
use sqlx::{Row, SqlitePool};
use std::fmt;
use std::sync::{Arc, Mutex};

#[derive(Debug)]
pub struct SqliteEmitterError(pub String);

impl fmt::Display for SqliteEmitterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "SqliteEmitter error: {}", self.0)
    }
}

impl std::error::Error for SqliteEmitterError {}

impl From<sqlx::Error> for SqliteEmitterError {
    fn from(err: sqlx::Error) -> Self {
        SqliteEmitterError(err.to_string())
    }
}

impl From<serde_json::Error> for SqliteEmitterError {
    fn from(err: serde_json::Error) -> Self {
        SqliteEmitterError(err.to_string())
    }
}

/// Emitter that reads from SQLite database and emits Wasot events
pub struct SqliteEmitter {
    /// Database connection pool
    pool: SqlitePool,
    /// Bus for events
    bus: Arc<Mutex<Bus<Event>>>,
}

impl SqliteEmitter {
    /// Create a new SQLite emitter from a database URL
    pub async fn from_database_url(database_url: &str) -> Result<Self, SqliteEmitterError> {
        let pool = SqlitePool::connect(database_url).await?;
        let bus = Arc::new(Mutex::new(Bus::new(1000)));

        Ok(Self { pool, bus })
    }

    /// Create a new SQLite emitter from an existing pool
    pub fn from_pool(pool: SqlitePool) -> Self {
        let bus = Arc::new(Mutex::new(Bus::new(1000)));
        Self { pool, bus }
    }
}

impl Emitter for SqliteEmitter {
    type Reader = BusReaderIterator;

    fn subscribe(&self) -> Self::Reader {
        let bus_reader = self.bus.lock().unwrap().add_rx();
        BusReaderIterator::new(bus_reader)
    }

    fn run(self) {
        // Use tokio runtime to handle async operations in sync context
        let rt = tokio::runtime::Runtime::new().unwrap();
        rt.block_on(async {
            // Get all elements ordered by line_idx and hierarchy
            let rows = match sqlx::query(
                r#"
            SELECT type_name, level, line_idx, inline_text, id, arguments, parent_id
            FROM elements 
            ORDER BY line_idx ASC
            "#,
            )
            .fetch_all(&self.pool)
            .await
            {
                Ok(rows) => rows,
                Err(e) => {
                    eprintln!("Error fetching rows from SQLite: {e}");
                    return;
                }
            };

            for row in rows {
                let type_name: String = row.get("type_name");
                let level: i32 = row.get("level");
                let line_idx: i32 = row.get("line_idx");
                let inline_text: Option<String> = row.get("inline_text");
                let id: Option<String> = row.get("id");
                let arguments_json: Option<String> = row.get("arguments");

                // Skip text elements for now - they should be handled separately
                if type_name == "text" {
                    continue;
                }

                // Parse arguments from JSON
                let arguments = if let Some(json_str) = arguments_json {
                    match serde_json::from_str::<ParsedArguments>(&json_str) {
                        Ok(parsed) => parsed,
                        Err(e) => {
                            eprintln!("Error parsing arguments JSON: {e}");
                            ParsedArguments {
                                args: Vec::new(),
                                kwargs: Vec::new(),
                            }
                        }
                    }
                } else {
                    ParsedArguments {
                        args: Vec::new(),
                        kwargs: Vec::new(),
                    }
                };

                // Create meta line
                let meta_line = MetaLine {
                    type_name,
                    level: level as usize,
                    line_index: Some(line_idx as usize),
                    inline_text: QuotedString::new(inline_text.unwrap_or_default(), false),
                    id: Id::new(id.unwrap_or_default()),
                    arguments,
                    is_void: false,
                };

                // Emit BlockStarted event
                let event = Event::BlockStarted { meta_line };
                if let Ok(mut bus) = self.bus.lock() {
                    bus.broadcast(event);
                }

                // TODO: Handle text content and BlockFinished events
                // This would require more complex logic to reconstruct the hierarchy
            }
        }); // End of rt.block_on
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sqlx::SqlitePool;
    use tempfile::NamedTempFile;

    #[test]
    fn test_sqlite_emitter_error_fmt() {
        let error = SqliteEmitterError("test error message".to_string());
        let formatted = format!("{error}");
        assert_eq!(formatted, "SqliteEmitter error: test error message");
    }

    #[test]
    fn test_sqlite_emitter_error_from_sqlx_error() {
        // Create a mock sqlx error by trying to use an invalid connection string
        let sqlx_err = sqlx::Error::Configuration("invalid config".into());
        let emitter_error = SqliteEmitterError::from(sqlx_err);
        // Just check that the error message is not empty and contains some relevant text
        assert!(!emitter_error.0.is_empty());
        assert!(emitter_error.0.contains("invalid config"));
    }

    #[test]
    fn test_sqlite_emitter_error_from_serde_json_error() {
        let json_str = "invalid json {";
        let json_error = serde_json::from_str::<serde_json::Value>(json_str).unwrap_err();
        let emitter_error = SqliteEmitterError::from(json_error);
        assert!(emitter_error.0.contains("expected"));
    }

    #[tokio::test]
    async fn test_sqlite_emitter_creation() {
        // Create a temporary database file
        let temp_file = NamedTempFile::new().unwrap();
        let database_url = format!("sqlite:{}", temp_file.path().to_string_lossy());

        // Test creation from database URL
        let result = SqliteEmitter::from_database_url(&database_url).await;
        assert!(result.is_ok());

        let emitter = result.unwrap();

        // Test that we can subscribe to the emitter
        let _receiver = emitter.subscribe();

        // Note: We can't easily test the run() method without setting up a proper database
        // with the expected schema, so this test just verifies creation and subscription work
    }

    #[tokio::test]
    async fn test_sqlite_emitter_from_pool() {
        // Create a temporary database file and pool
        let temp_file = NamedTempFile::new().unwrap();
        let database_url = format!("sqlite:{}", temp_file.path().to_string_lossy());
        let pool = SqlitePool::connect(&database_url).await.unwrap();

        // Test creation from pool
        let emitter = SqliteEmitter::from_pool(pool);

        // Test that we can subscribe to the emitter
        let _receiver = emitter.subscribe();
    }

    #[test]
    fn test_sqlite_emitter_error_is_std_error() {
        let error = SqliteEmitterError("test".to_string());
        // Test that it implements std::error::Error trait
        let _: &dyn std::error::Error = &error;
    }

    #[tokio::test]
    async fn test_sqlite_emitter_subscribe() {
        let temp_file = NamedTempFile::new().unwrap();
        let database_url = format!("sqlite:{}", temp_file.path().to_string_lossy());
        let pool = SqlitePool::connect(&database_url).await.unwrap();
        let emitter = SqliteEmitter::from_pool(pool);

        // Test multiple subscriptions
        let _reader1 = emitter.subscribe();
        let _reader2 = emitter.subscribe();

        // Both readers should be valid BusReaderIterator instances
    }

    #[tokio::test]
    async fn test_sqlite_emitter_invalid_database_url() {
        // Test with an invalid database URL
        let result = SqliteEmitter::from_database_url("invalid://not_a_database").await;
        assert!(result.is_err());

        if let Err(error) = result {
            assert!(!error.0.is_empty());
        }
    }
}
