use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex as StdMutex};

use crate::driver::QueryResult;
use crate::error::Result;
use crate::values::Value;

/// Per-request context passed to middleware methods.
///
/// A type-map that allows middleware to store and retrieve typed state
/// scoped to a single request/transaction/scope. Unlike `&self` (which is
/// shared globally across all requests), `RequestContext` is created per
/// scope boundary and is safe for per-request accumulation.
pub struct RequestContext {
    data: StdMutex<HashMap<TypeId, Box<dyn Any + Send>>>,
}

impl RequestContext {
    pub fn new() -> Self {
        Self {
            data: StdMutex::new(HashMap::new()),
        }
    }

    /// Insert or overwrite a typed value.
    pub fn set<T: Any + Send + 'static>(&self, value: T) {
        self.data
            .lock()
            .unwrap()
            .insert(TypeId::of::<T>(), Box::new(value));
    }

    /// Get a clone of a typed value, if present.
    pub fn get<T: Any + Send + Clone + 'static>(&self) -> Option<T> {
        self.data
            .lock()
            .unwrap()
            .get(&TypeId::of::<T>())
            .and_then(|v| v.downcast_ref::<T>())
            .cloned()
    }

    /// Mutate a typed value in place. Inserts `Default` if absent.
    pub fn with<T: Any + Send + Default + 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
        let mut map = self.data.lock().unwrap();
        let entry = map
            .entry(TypeId::of::<T>())
            .or_insert_with(|| Box::new(T::default()));
        f(entry.downcast_mut::<T>().unwrap())
    }
}

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

/// Operation type for method-level middleware dispatch.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationType {
    Find,
    Create,
    Update,
    Delete,
    Other,
}

/// Middleware trait for intercepting database operations.
///
/// Implements a chain-of-responsibility pattern. Each middleware wraps the next,
/// and can modify behavior before/after the actual database call.
///
/// Method-level hooks (on_find, on_create, etc.) default to on_execute.
/// Override specific hooks to intercept only those operations.
pub trait Middleware: Send + Sync + 'static {
    fn name(&self) -> &str;

    /// Intercept SQL execution (lowest level, catches all operations).
    ///
    /// `ctx` is a per-scope context (e.g. per-transaction) for accumulating
    /// request-scoped state. Use `ctx.with::<T, _>(|s| ...)` to read/write typed state.
    /// `self` should only hold immutable config or global shared resources.
    fn on_execute<'a>(
        &'a self,
        ctx: &'a RequestContext,
        sql: &'a str,
        params: &'a [Value],
        next: NextExecute<'a>,
    ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>> {
        let _ = ctx;
        next.run(sql, params)
    }

    /// Intercept find operations. Default: on_execute.
    fn on_find<'a>(
        &'a self,
        ctx: &'a RequestContext,
        _table: &'a str,
        sql: &'a str,
        params: &'a [Value],
        next: NextExecute<'a>,
    ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>> {
        self.on_execute(ctx, sql, params, next)
    }

    /// Intercept create operations. Default: on_execute.
    fn on_create<'a>(
        &'a self,
        ctx: &'a RequestContext,
        _table: &'a str,
        sql: &'a str,
        params: &'a [Value],
        next: NextExecute<'a>,
    ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>> {
        self.on_execute(ctx, sql, params, next)
    }

    /// Intercept update operations. Default: on_execute.
    fn on_update<'a>(
        &'a self,
        ctx: &'a RequestContext,
        _table: &'a str,
        sql: &'a str,
        params: &'a [Value],
        next: NextExecute<'a>,
    ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>> {
        self.on_execute(ctx, sql, params, next)
    }

    /// Intercept delete operations. Default: on_execute.
    fn on_delete<'a>(
        &'a self,
        ctx: &'a RequestContext,
        _table: &'a str,
        sql: &'a str,
        params: &'a [Value],
        next: NextExecute<'a>,
    ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>> {
        self.on_execute(ctx, sql, params, next)
    }
}

/// Next handler in the middleware chain for execute operations.
pub struct NextExecute<'a> {
    inner: Arc<
        dyn Fn(
                &'a str,
                &'a [Value],
            ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>>
            + Send
            + Sync
            + 'a,
    >,
}

impl<'a> NextExecute<'a> {
    pub fn new<F>(f: F) -> Self
    where
        F: Fn(
                &'a str,
                &'a [Value],
            ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>>
            + Send
            + Sync
            + 'a,
    {
        Self { inner: Arc::new(f) }
    }

    pub fn run(
        self,
        sql: &'a str,
        params: &'a [Value],
    ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>> {
        (self.inner)(sql, params)
    }
}

impl<'a> Clone for NextExecute<'a> {
    fn clone(&self) -> Self {
        Self {
            inner: Arc::clone(&self.inner),
        }
    }
}

/// A chain of middlewares.
#[derive(Default)]
pub struct MiddlewareChain {
    middlewares: Vec<Arc<dyn Middleware>>,
}

impl MiddlewareChain {
    pub fn new() -> Self {
        Self::default()
    }

    /// Add a middleware to the chain (outermost = last added).
    pub fn add(&mut self, middleware: impl Middleware) {
        self.middlewares.push(Arc::new(middleware));
    }

    /// Remove a middleware by name. Returns true if found.
    pub fn remove(&mut self, name: &str) -> bool {
        let before = self.middlewares.len();
        self.middlewares.retain(|m| m.name() != name);
        self.middlewares.len() < before
    }

    /// Get the list of registered middlewares.
    pub fn list(&self) -> &[Arc<dyn Middleware>] {
        &self.middlewares
    }

    pub fn is_empty(&self) -> bool {
        self.middlewares.is_empty()
    }
}

/// Example: Statistics middleware that counts operations.
pub struct StatisticsMiddleware {
    name: String,
}

impl StatisticsMiddleware {
    pub fn new() -> Self {
        Self {
            name: "statistics".to_string(),
        }
    }
}

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

impl Middleware for StatisticsMiddleware {
    fn name(&self) -> &str {
        &self.name
    }

    fn on_execute<'a>(
        &'a self,
        _ctx: &'a RequestContext,
        sql: &'a str,
        params: &'a [Value],
        next: NextExecute<'a>,
    ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>> {
        Box::pin(async move {
            let start = std::time::Instant::now();
            let result = next.run(sql, params).await;
            let elapsed = start.elapsed();
            tracing::debug!(
                sql = sql,
                elapsed_ms = elapsed.as_millis() as u64,
                "SQL executed"
            );
            result
        })
    }
}

#[cfg(test)]
mod tests {
    use std::sync::Mutex;

    use super::*;

    struct TestMiddleware;

    impl Middleware for TestMiddleware {
        fn name(&self) -> &str {
            "test"
        }
    }

    /// Records on_execute calls for testing.
    struct RecordingMiddleware {
        name: String,
        calls: Arc<Mutex<Vec<String>>>,
    }

    impl RecordingMiddleware {
        fn new(name: &str, calls: Arc<Mutex<Vec<String>>>) -> Self {
            Self {
                name: name.to_string(),
                calls,
            }
        }
    }

    impl Middleware for RecordingMiddleware {
        fn name(&self) -> &str {
            &self.name
        }

        fn on_execute<'a>(
            &'a self,
            _ctx: &'a RequestContext,
            sql: &'a str,
            params: &'a [Value],
            next: NextExecute<'a>,
        ) -> Pin<Box<dyn Future<Output = Result<QueryResult>> + Send + 'a>> {
            let name = self.name.clone();
            let calls = Arc::clone(&self.calls);
            Box::pin(async move {
                calls.lock().unwrap().push(format!("{}:{}", name, sql));
                next.run(sql, params).await
            })
        }
    }

    #[test]
    fn test_middleware_chain() {
        let mut chain = MiddlewareChain::new();
        assert!(chain.is_empty());

        chain.add(TestMiddleware);
        assert_eq!(chain.list().len(), 1);
        assert_eq!(chain.list()[0].name(), "test");

        chain.add(StatisticsMiddleware::new());
        assert_eq!(chain.list().len(), 2);

        assert!(chain.remove("test"));
        assert_eq!(chain.list().len(), 1);
        assert_eq!(chain.list()[0].name(), "statistics");
    }

    #[test]
    fn test_remove_nonexistent_returns_false() {
        let mut chain = MiddlewareChain::new();
        chain.add(TestMiddleware);
        assert!(!chain.remove("nonexistent"));
        assert_eq!(chain.list().len(), 1);
    }

    /// Middleware that always returns an error.
    struct ErrorMiddleware;

    impl Middleware for ErrorMiddleware {
        fn name(&self) -> &str {
            "error"
        }

        fn on_execute<'a>(
            &'a self,
            _ctx: &'a RequestContext,
            _sql: &'a str,
            _params: &'a [Value],
            _next: NextExecute<'a>,
        ) -> Pin<Box<dyn Future<Output = crate::error::Result<QueryResult>> + Send + 'a>> {
            Box::pin(async move {
                Err(crate::error::Error::Transaction(
                    "middleware error".to_string(),
                ))
            })
        }
    }

    #[tokio::test]
    async fn test_middleware_returns_error() {
        let mw = ErrorMiddleware;
        let core = NextExecute::new(|_sql, _params| {
            Box::pin(async move {
                Ok(QueryResult {
                    rows: vec![],
                    rows_affected: 1,
                })
            })
        });
        let ctx = RequestContext::new();
        let result = mw.on_execute(&ctx, "SELECT 1", &[], core).await;
        assert!(result.is_err());
        match result.unwrap_err() {
            crate::error::Error::Transaction(msg) => assert_eq!(msg, "middleware error"),
            other => panic!("Expected Transaction error, got {:?}", other),
        }
    }

    /// Middleware that modifies SQL before passing to next.
    /// Uses a static string for the replacement to satisfy lifetime requirements.
    struct SqlModifyingMiddleware;

    impl Middleware for SqlModifyingMiddleware {
        fn name(&self) -> &str {
            "sql_modifier"
        }

        fn on_execute<'a>(
            &'a self,
            _ctx: &'a RequestContext,
            sql: &'a str,
            params: &'a [Value],
            next: NextExecute<'a>,
        ) -> Pin<Box<dyn Future<Output = crate::error::Result<QueryResult>> + Send + 'a>> {
            let modified_sql: &'a str = if sql == "SELECT 1" { "SELECT 42" } else { sql };
            Box::pin(async move { next.run(modified_sql, params).await })
        }
    }

    #[tokio::test]
    async fn test_middleware_modifies_sql() {
        let mw = SqlModifyingMiddleware;
        let core = NextExecute::new(|sql, _params| {
            Box::pin(async move {
                assert_eq!(sql, "SELECT 42");
                Ok(QueryResult {
                    rows: vec![],
                    rows_affected: 1,
                })
            })
        });
        let ctx = RequestContext::new();
        let result = mw.on_execute(&ctx, "SELECT 1", &[], core).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_on_execute_flow() {
        let calls = Arc::new(Mutex::new(Vec::new()));
        let mw = RecordingMiddleware::new("rec", Arc::clone(&calls));
        let core = NextExecute::new(|_sql, _params| {
            Box::pin(async move {
                Ok(QueryResult {
                    rows: vec![],
                    rows_affected: 1,
                })
            })
        });
        let ctx = RequestContext::new();
        let result = mw.on_execute(&ctx, "SELECT 1", &[], core).await;
        assert!(result.is_ok());
        assert_eq!(result.unwrap().rows_affected, 1);
        let c = calls.lock().unwrap();
        assert_eq!(c.len(), 1);
        assert_eq!(c[0], "rec:SELECT 1");
    }

    #[tokio::test]
    async fn test_multiple_middleware_execution_order() {
        let calls = Arc::new(Mutex::new(Vec::new()));
        let m1 = RecordingMiddleware::new("first", Arc::clone(&calls));
        let m2 = RecordingMiddleware::new("second", Arc::clone(&calls));
        let ctx = RequestContext::new();
        let next2 = NextExecute::new(|s, p| {
            let core = NextExecute::new(|_sql, _params| {
                Box::pin(async move {
                    Ok(QueryResult {
                        rows: vec![],
                        rows_affected: 0,
                    })
                })
            });
            m2.on_execute(&ctx, s, p, core)
        });
        let result = m1.on_execute(&ctx, "SELECT * FROM t", &[], next2).await;
        assert!(result.is_ok());
        let c = calls.lock().unwrap();
        assert_eq!(c.len(), 2);
        assert_eq!(c[0], "first:SELECT * FROM t");
        assert_eq!(c[1], "second:SELECT * FROM t");
    }

    #[tokio::test]
    async fn test_statistics_middleware_forwards() {
        let mw = StatisticsMiddleware::new();
        let core = NextExecute::new(|_sql, _params| {
            Box::pin(async move {
                Ok(QueryResult {
                    rows: vec![],
                    rows_affected: 42,
                })
            })
        });
        let ctx = RequestContext::new();
        let result = mw.on_execute(&ctx, "SELECT 1", &[], core).await;
        assert!(result.is_ok());
        assert_eq!(result.unwrap().rows_affected, 42);
    }

    /// Middleware that only overrides on_find.
    struct FindOnlyMiddleware {
        name: String,
        calls: Arc<Mutex<Vec<String>>>,
    }

    impl FindOnlyMiddleware {
        fn new(name: &str, calls: Arc<Mutex<Vec<String>>>) -> Self {
            Self {
                name: name.to_string(),
                calls,
            }
        }
    }

    impl Middleware for FindOnlyMiddleware {
        fn name(&self) -> &str {
            &self.name
        }

        fn on_find<'a>(
            &'a self,
            _ctx: &'a RequestContext,
            table: &'a str,
            sql: &'a str,
            params: &'a [Value],
            next: NextExecute<'a>,
        ) -> Pin<Box<dyn Future<Output = crate::error::Result<QueryResult>> + Send + 'a>> {
            let calls = Arc::clone(&self.calls);
            let table = table.to_string();
            Box::pin(async move {
                calls
                    .lock()
                    .unwrap()
                    .push(format!("on_find:{}:{}", table, sql));
                next.run(sql, params).await
            })
        }
    }

    /// Middleware that only overrides on_create.
    struct CreateOnlyMiddleware {
        name: String,
        calls: Arc<Mutex<Vec<String>>>,
    }

    impl CreateOnlyMiddleware {
        fn new(name: &str, calls: Arc<Mutex<Vec<String>>>) -> Self {
            Self {
                name: name.to_string(),
                calls,
            }
        }
    }

    impl Middleware for CreateOnlyMiddleware {
        fn name(&self) -> &str {
            &self.name
        }

        fn on_create<'a>(
            &'a self,
            _ctx: &'a RequestContext,
            table: &'a str,
            sql: &'a str,
            params: &'a [Value],
            next: NextExecute<'a>,
        ) -> Pin<Box<dyn Future<Output = crate::error::Result<QueryResult>> + Send + 'a>> {
            let calls = Arc::clone(&self.calls);
            let table = table.to_string();
            Box::pin(async move {
                calls
                    .lock()
                    .unwrap()
                    .push(format!("on_create:{}:{}", table, sql));
                next.run(sql, params).await
            })
        }
    }

    #[tokio::test]
    async fn test_on_find_hook_called() {
        let calls = Arc::new(Mutex::new(Vec::new()));
        let mw = FindOnlyMiddleware::new("find_mw", Arc::clone(&calls));
        let core = NextExecute::new(|_sql, _params| {
            Box::pin(async move {
                Ok(QueryResult {
                    rows: vec![],
                    rows_affected: 0,
                })
            })
        });
        let ctx = RequestContext::new();
        let _ = mw
            .on_find(&ctx, "users", "SELECT * FROM users", &[], core)
            .await;
        let c = calls.lock().unwrap();
        assert_eq!(c.len(), 1);
        assert_eq!(c[0], "on_find:users:SELECT * FROM users");
    }

    #[tokio::test]
    async fn test_on_create_hook_called() {
        let calls = Arc::new(Mutex::new(Vec::new()));
        let mw = CreateOnlyMiddleware::new("create_mw", Arc::clone(&calls));
        let core = NextExecute::new(|_sql, _params| {
            Box::pin(async move {
                Ok(QueryResult {
                    rows: vec![],
                    rows_affected: 1,
                })
            })
        });
        let ctx = RequestContext::new();
        let _ = mw
            .on_create(
                &ctx,
                "items",
                "INSERT INTO items (id) VALUES (1)",
                &[],
                core,
            )
            .await;
        let c = calls.lock().unwrap();
        assert_eq!(c.len(), 1);
        assert!(c[0].starts_with("on_create:items:"));
    }

    #[tokio::test]
    async fn test_default_fallback_to_on_execute() {
        // Middleware with only on_execute (default) - find/create should still work via fallback
        let mw = RecordingMiddleware::new("rec", Arc::new(Mutex::new(Vec::new())));
        let core = NextExecute::new(|_sql, _params| {
            Box::pin(async move {
                Ok(QueryResult {
                    rows: vec![],
                    rows_affected: 1,
                })
            })
        });
        let ctx = RequestContext::new();
        let result = mw
            .on_find(&ctx, "users", "SELECT * FROM users", &[], core)
            .await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_next_execute_forwarding() {
        let params = vec![Value::Int(1)];
        let core = NextExecute::new(|sql, p| {
            Box::pin(async move {
                assert_eq!(sql, "INSERT INTO t VALUES (?)");
                assert_eq!(p.len(), 1);
                Ok(QueryResult {
                    rows: vec![],
                    rows_affected: 1,
                })
            })
        });
        let result = core.run("INSERT INTO t VALUES (?)", &params).await;
        assert!(result.is_ok());
        assert_eq!(result.unwrap().rows_affected, 1);
    }
}
