use crate::*;

#[test]
fn server_partial_eq() {
    let server1: Server = Server::default();
    let server2: Server = Server::default();
    assert_eq!(server1, server2);
    let server1_clone: Server = server1.clone();
    assert_eq!(server1, server1_clone);
}

#[test]
fn server_from_address() {
    let mut server: Server = Server::default();
    server.set_request_config(RequestConfig::default());
    let server_address: usize = (&server).into();
    let server_from_addr: Server = server_address.into();
    assert_eq!(
        server.get_request_config(),
        server_from_addr.get_request_config()
    );
}

#[test]
fn server_ref_from_address() {
    let mut server: Server = Server::default();
    server.set_server_config(ServerConfig::default());
    let server_address: usize = (&server).into();
    let server_ref: &Server = server_address.into();
    assert_eq!(server.get_server_config(), server_ref.get_server_config());
}

#[test]
fn server_mut_from_address() {
    let mut server: Server = Server::default();
    let server_address: usize = (&mut server).into();
    let server_mut: &mut Server = server_address.into();
    let mut config: ServerConfig = ServerConfig::default();
    config.set_nodelay(Some(true));
    server_mut.set_server_config(config);
    assert!(server_mut.get_server_config().try_get_nodelay().is_some());
}

#[test]
fn server_from_server_config() {
    let mut server_config: ServerConfig = ServerConfig::default();
    server_config.set_nodelay(Some(true));
    let server: Server = server_config.clone().into();
    assert_eq!(server.get_request_config(), &RequestConfig::default());
    assert_eq!(server.get_server_config(), &server_config);
    assert!(server.get_task_panic().is_empty());
    assert!(server.get_request_error().is_empty());
    assert!(server.get_request_middleware().is_empty());
    assert!(server.get_response_middleware().is_empty());
}

#[test]
fn server_from_request_config() {
    let mut request_config: RequestConfig = RequestConfig::default();
    request_config.set_buffer_size(KB_1);
    let server: Server = request_config.into();
    assert_eq!(server.get_request_config(), &request_config);
    assert_eq!(server.get_server_config(), &ServerConfig::default());
    assert!(server.get_task_panic().is_empty());
    assert!(server.get_request_error().is_empty());
    assert!(server.get_request_middleware().is_empty());
    assert!(server.get_response_middleware().is_empty());
}

#[test]
fn server_inner_partial_eq() {
    let inner1: Server = Server::default();
    let inner2: Server = Server::default();
    assert_eq!(inner1, inner2);
}

#[test]
fn server_ref_into_address() {
    let server: Server = Server::default();
    let server_address: usize = (&server).into();
    assert!(server_address > 0);
}

#[test]
fn server_mut_into_address() {
    let mut server: Server = Server::default();
    let server_address: usize = (&mut server).into();
    assert!(server_address > 0);
}

#[test]
fn server_as_ref() {
    let mut server: Server = Server::default();
    server.set_server_config(ServerConfig::default());
    let server_ref: &Server = server.as_ref();
    assert_eq!(server.get_server_config(), server_ref.get_server_config());
    assert_eq!(server.get_request_config(), server_ref.get_request_config());
}

#[test]
fn server_as_mut() {
    let mut server: Server = Server::default();
    let server_mut: &mut Server = server.as_mut();
    let mut config: ServerConfig = ServerConfig::default();
    config.set_nodelay(Some(true));
    server_mut.set_server_config(config);
    assert!(server.get_server_config().try_get_nodelay().is_some());
}

struct TestSendRoute;

impl ServerHook for TestSendRoute {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, _: &mut Stream, _: &mut Context) -> Status {
        Status::Continue
    }
}

#[test]
fn server_send_sync() {
    fn assert_send<T: Send>() {}
    fn assert_sync<T: Sync>() {}
    fn assert_send_sync<T: Send + Sync>() {}
    assert_send::<Server>();
    assert_sync::<Server>();
    assert_send_sync::<Server>();
}

#[tokio::test]
async fn server_clone_across_threads() {
    let mut server: Server = Server::default();
    server.route::<TestSendRoute>("/test");
    let server_clone: Server = server.clone();
    let handle: JoinHandle<&'static str> = spawn(async move {
        let _server_in_thread: Server = server_clone;
        "success"
    });
    let result: &'static str = handle.await.unwrap();
    assert_eq!(result, "success");
}

#[tokio::test]
async fn server_share_across_threads() {
    let mut server: Server = Server::default();
    server.route::<TestSendRoute>("/test");
    let server: Arc<Server> = Arc::new(server);
    let server1: Arc<Server> = server.clone();
    let server2: Arc<Server> = server.clone();
    let handle1: JoinHandle<&'static str> = spawn(async move {
        let _server_in_thread1: Arc<Server> = server1;
        "thread1"
    });
    let handle2: JoinHandle<&'static str> = spawn(async move {
        let _server_in_thread2: Arc<Server> = server2;
        "thread2"
    });
    let result1: &'static str = handle1.await.unwrap();
    let result2: &'static str = handle2.await.unwrap();
    assert_eq!(result1, "thread1");
    assert_eq!(result2, "thread2");
}

static SERVER_REF: OnceLock<Server> = OnceLock::new();

struct TaskPanicHook {
    response_body: String,
    content_type: String,
}

impl ServerHook for TaskPanicHook {
    async fn new(_: &mut Stream, ctx: &mut Context) -> Self {
        let error: PanicData = ctx.try_get_task_panic_data().unwrap_or_default();
        let response_body: String = error.to_string();
        let content_type: String = ContentType::format_content_type_with_charset(TEXT_PLAIN, UTF8);
        Self {
            response_body,
            content_type,
        }
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let data: Vec<u8> = ctx
            .get_mut_response()
            .set_version(HttpVersion::Http1_1)
            .set_status_code(500)
            .clear_headers()
            .set_header(SERVER, HYPERLANE)
            .set_header(CONTENT_TYPE, &self.content_type)
            .set_body(&self.response_body)
            .build();
        if stream.try_send(data).await.is_err() {
            stream.set_closed(true);
            return Status::Reject;
        }
        Status::Continue
    }
}

struct RequestErrorHook {
    response_status_code: ResponseStatusCode,
    response_body: String,
}

impl ServerHook for RequestErrorHook {
    async fn new(_: &mut Stream, ctx: &mut Context) -> Self {
        let request_error: RequestError = ctx.try_get_request_error_data().unwrap_or_default();
        Self {
            response_status_code: request_error.get_http_status_code(),
            response_body: request_error.to_string(),
        }
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let data: Vec<u8> = ctx
            .get_mut_response()
            .set_version(HttpVersion::Http1_1)
            .set_status_code(self.response_status_code)
            .set_body(self.response_body)
            .build();
        if stream.try_send(data).await.is_err() {
            stream.set_closed(true);
            return Status::Reject;
        }
        Status::Continue
    }
}

struct RequestMiddleware {
    socket_addr: String,
}

impl ServerHook for RequestMiddleware {
    async fn new(stream: &mut Stream, _: &mut Context) -> Self {
        let mut socket_addr: String = String::new();

        socket_addr = stream
            .get_stream()
            .peer_addr()
            .map(|data| data.to_string())
            .unwrap_or_default();

        Self { socket_addr }
    }

    async fn handle(self, _: &mut Stream, ctx: &mut Context) -> Status {
        ctx.get_mut_response()
            .set_version(HttpVersion::Http1_1)
            .set_status_code(200)
            .set_header(SERVER, HYPERLANE)
            .set_header(CONNECTION, KEEP_ALIVE)
            .set_header(CONTENT_TYPE, TEXT_PLAIN)
            .set_header(ACCESS_CONTROL_ALLOW_ORIGIN, WILDCARD_ANY)
            .set_header("SocketAddr", &self.socket_addr);
        Status::Continue
    }
}

struct UpgradeMiddleware;

impl ServerHook for UpgradeMiddleware {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        if !ctx.get_request().is_ws_upgrade_type() {
            return Status::Continue;
        }
        if let Some(key) = &ctx.get_request().try_get_header_back(SEC_WEBSOCKET_KEY) {
            let accept_key: String = WebSocketFrame::generate_accept_key(key);
            let data: Vec<u8> = ctx
                .get_mut_response()
                .set_version(HttpVersion::Http1_1)
                .set_status_code(101)
                .set_header(UPGRADE, WEBSOCKET)
                .set_header(CONNECTION, UPGRADE)
                .set_header(SEC_WEBSOCKET_ACCEPT, &accept_key)
                .set_body(Vec::new())
                .build();
            if stream.try_send(data).await.is_err() {
                stream.set_closed(true);
                return Status::Reject;
            }
        }
        Status::Continue
    }
}

struct ResponseMiddleware;

impl ServerHook for ResponseMiddleware {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        if ctx.get_request().is_ws_upgrade_type() {
            return Status::Continue;
        }
        let data: Vec<u8> = ctx.get_mut_response().build();
        if stream.try_send(data).await.is_err() {
            stream.set_closed(true);
            return Status::Reject;
        }
        Status::Continue
    }
}

struct RootRoute {
    response_body: String,
    cookie1: String,
    cookie2: String,
}

impl ServerHook for RootRoute {
    async fn new(_: &mut Stream, ctx: &mut Context) -> Self {
        let response_body: String = format!("Hello hyperlane => {}", ctx.get_request().get_path());
        let cookie1: String = CookieBuilder::new("key1", "value1").http_only().build();
        let cookie2: String = CookieBuilder::new("key2", "value2").http_only().build();
        Self {
            response_body,
            cookie1,
            cookie2,
        }
    }

    async fn handle(self, _: &mut Stream, ctx: &mut Context) -> Status {
        ctx.get_mut_response()
            .add_header(SET_COOKIE, &self.cookie1)
            .add_header(SET_COOKIE, &self.cookie2)
            .set_body(&self.response_body);
        Status::Continue
    }
}

struct SseRoute;

impl ServerHook for SseRoute {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let data: Vec<u8> = ctx
            .get_mut_response()
            .set_header(CONTENT_TYPE, TEXT_EVENT_STREAM)
            .set_body(Vec::new())
            .build();
        if stream.try_send(data).await.is_err() {
            stream.set_closed(true);
            return Status::Reject;
        }
        for i in 0..10 {
            let body: String = format!("data:{i}{HTTP_DOUBLE_BR}");
            if stream.try_send(&body).await.is_err() {
                break;
            }
        }
        stream.set_closed(true);
        Status::Reject
    }
}

struct WebsocketRoute;

impl WebsocketRoute {
    async fn try_send_body_hook(
        &self,
        stream: &mut Stream,
        ctx: &mut Context,
    ) -> Result<(), ResponseError> {
        let send_result: Result<(), ResponseError> = if ctx.get_request().is_ws_upgrade_type() {
            let body: &ResponseBody = ctx.get_response().get_body();
            let frame_list: Vec<ResponseBody> = WebSocketFrame::create_frame_list(body);
            stream.try_send_list(&frame_list).await
        } else {
            let body: &Vec<u8> = ctx.get_response().get_body();
            stream.try_send(body).await
        };
        if send_result.is_err() {
            stream.set_closed(true);
        }
        send_result
    }
}

impl ServerHook for WebsocketRoute {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        while let Ok(body) = stream.try_get_websocket_request().await {
            ctx.get_mut_response().set_body(body);
            if self.try_send_body_hook(stream, ctx).await.is_err() {
                return Status::Reject;
            }
        }
        Status::Continue
    }
}

struct DynamicRoute {
    params: RouteParams,
}

impl ServerHook for DynamicRoute {
    async fn new(_: &mut Stream, ctx: &mut Context) -> Self {
        Self {
            params: ctx.get_route_params().clone(),
        }
    }

    async fn handle(mut self, _: &mut Stream, _: &mut Context) -> Status {
        self.params.insert("key".to_owned(), "value".to_owned());
        panic!("Test panic {:?}", self.params);
    }
}

struct GetAllRoutes;

impl ServerHook for GetAllRoutes {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, _: &mut Stream, ctx: &mut Context) -> Status {
        if let Some(server) = SERVER_REF.get() {
            let route_matcher: &RouteMatcher = server.get_route_matcher();
            let mut response_body: String = String::new();
            for key in route_matcher.get_static_route().keys() {
                response_body.push_str(&format!("Static route: {key}\n"));
            }
            for value in route_matcher.get_dynamic_route().values() {
                for (route_pattern, _) in value {
                    response_body.push_str(&format!("Dynamic route: {route_pattern}\n"));
                }
            }
            for value in route_matcher.get_regex_route().values() {
                for (route_pattern, _) in value {
                    response_body.push_str(&format!("Regex route: {route_pattern}\n"));
                }
            }
            ctx.get_mut_response().set_body(&response_body);
        }
        Status::Continue
    }
}

#[tokio::test]
async fn main() {
    let mut server: Server = Server::default();
    let mut server_config: ServerConfig = ServerConfig::default();
    server_config
        .set_address(Server::format_bind_address(DEFAULT_HOST, 80))
        .set_nodelay(Some(false));
    server.server_config(server_config);
    server.task_panic::<TaskPanicHook>();
    server.request_error::<RequestErrorHook>();
    server.request_middleware::<RequestMiddleware>();
    server.request_middleware::<UpgradeMiddleware>();
    server.response_middleware::<ResponseMiddleware>();
    server.route::<RootRoute>("/");
    server.route::<SseRoute>("/sse");
    server.route::<WebsocketRoute>("/websocket");
    server.route::<GetAllRoutes>("/get/all/routes");
    server.route::<DynamicRoute>("/dynamic/{routing}");
    server.route::<DynamicRoute>("/regex/{file:^.*$}");
    let _ = SERVER_REF.set(server.clone());
    let server_control_hook_1: ServerControlHook = server.run().await.unwrap_or_default();
    let server_control_hook_2: ServerControlHook = server_control_hook_1.clone();
    spawn(async move {
        sleep(Duration::from_secs(60)).await;
        server_control_hook_2.shutdown().await;
    });
    server_control_hook_1.wait().await;
}
