use std::sync::Arc;

use crate::{
    middleware::{Middleware, Next},
};
use crate::request::request::Request;
use crate::response::response::Response;

#[derive(Debug, Clone)]
pub struct WithState<T: Send + Sync + 'static> {
    extension: AppState<T>,
}

impl<T: Send + Sync + 'static> WithState<T> {
    pub fn new(extension: T) -> Self {
        WithState {
            extension: AppState {
                inner: Arc::new(extension),
            },
        }
    }

    async fn append_extension<'a>(&'a self, mut ctx: Request, next: Next<'a>) -> Response {
        ctx.insert_extension(self.extension.clone());
        next.run(ctx).await
    }

    pub(crate) fn get_state(ctx: &Request) -> Option<&T> {
        ctx.get_extension::<AppState<T>>().map(|o| o.inner.as_ref())
    }
}

#[crate::async_trait]
impl<T: Send + Sync + 'static + Clone> Middleware for WithState<T> {
    async fn handle<'a>(&'a self, ctx: Request, next: Next<'a>) -> Response {
        self.append_extension(ctx, next).await
    }
}

#[derive(Debug)]
pub(crate) struct AppState<T: Send + Sync + 'static> {
    pub(crate) inner: Arc<T>,
}

impl<T> Clone for AppState<T>
    where
        T: Send + Sync + 'static,
{
    fn clone(&self) -> Self {
        AppState {
            inner: self.inner.clone(),
        }
    }
}
