use std::pin::Pin;
use std::future::Future;
use std::task::{Context, Poll};
use std::sync::{Arc, Mutex};
use tonic::{transport::Server, Request, Response, Status};
use roto_tonic::RotoCodec;
use hello::{HelloWorldService, OwnedHelloRequest, OwnedHelloResponse};
use tower::Service;
use bytes::{Bytes, BytesMut, Buf, BufMut};
use tonic::body::BoxBody;
use futures_util::StreamExt;
use roto_runtime::{RotoOwned, RotoMessage};
use http_body_util::BodyExt;
use http_body::Body;

pub use roto_tonic::{BufferPool, StatusBody};

pub mod hello {
    include!(concat!(env!("OUT_DIR"), "/hello.rs"));
}

#[derive(Clone)]
pub struct MyHelloWorld {
    pool: Arc<BufferPool>,
}

impl MyHelloWorld {
    pub fn new(pool: Arc<BufferPool>) -> Self {
        Self { pool }
    }
}

#[tonic::async_trait]
impl HelloWorldService for MyHelloWorld {
    async fn hello_world(
        &self,
        request: Request<OwnedHelloRequest>,
    ) -> Result<Response<OwnedHelloResponse>, Status> {
        let req = request.into_inner();
        let reader = req.reader();
        let name = reader.name().unwrap_or("Unknown");

        let mut buf = self.pool.get();
        buf.resize(1024, 0);
        let slice = hello::HelloResponseBuilder::builder(&mut buf[..])
            .message(&format!("Hello {}!", name)).unwrap()
            .finish().unwrap();

        let res_len = slice.len();
        let response_bytes = buf.split_to(res_len).freeze();
        self.pool.put(buf);

        let reply = OwnedHelloResponse {
            data: response_bytes,
        };

        Ok(Response::new(reply))
    }
}

// --- Tonic Glue ---

#[derive(Clone)]
pub struct HelloWorldServer {
    inner: Arc<MyHelloWorld>,
    pool: Arc<BufferPool>,
}

impl HelloWorldServer {
    pub fn new(inner: MyHelloWorld, pool: Arc<BufferPool>) -> Self {
        Self { inner: Arc::new(inner), pool }
    }
}

impl tonic::server::NamedService for HelloWorldServer {
    const NAME: &'static str = "hello.HelloWorldService";
}

impl Service<http::Request<BoxBody>> for HelloWorldServer {
    type Response = http::Response<BoxBody>;
    type Error = std::convert::Infallible;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;

    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
        let inner = self.inner.clone();
        let pool = self.pool.clone();
        println!("Server received request: {} {}", req.method(), req.uri());

        Box::pin(async move {
            let body = req.into_body();
            let mut buf = pool.get();
            let mut stream = body;
            while let Some(frame_result) = stream.frame().await {
                let frame = frame_result.map_err(|e| {
                    println!("Body frame error: {}", e);
                    panic!("Body frame error: {}", e);
                })?;
                if let Some(data) = frame.data_ref() {
                    buf.put(data.clone());
                }
            }

            let total_len = buf.len();
            let bytes_vec = buf.split_to(total_len).freeze();
            pool.put(buf);
            println!("Collected body bytes: {} bytes", bytes_vec.len());

            if bytes_vec.len() < 5 {
                println!("Body too short: {} bytes", bytes_vec.len());
                let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
                return Ok(http::Response::builder()
                    .status(200)
                    .body(res_body)
                    .unwrap());
            }

            println!("Decoding request from {} bytes", bytes_vec.len() - 5);
            let request_msg = match OwnedHelloRequest::decode(bytes_vec.slice(5..)) {
                Ok(msg) => msg,
                Err(e) => {
                    println!("Decode error: {}", e);
                    let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
                    return Ok(http::Response::builder().status(200).body(res_body).unwrap());
                }
            };

            println!("Request decoded successfully");
            let response = match inner.hello_world(Request::new(request_msg)).await {
                Ok(res) => res,
                Err(e) => {
                    println!("Service error: {}", e);
                    let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
                    return Ok(http::Response::builder().status(200).body(res_body).unwrap());
                }
            };

            let response_msg = response.into_inner();
            let response_bytes = response_msg.bytes();
            println!("Service responded with {} bytes", response_bytes.len());

            let mut res_buf = pool.get();
            res_buf.put_u8(0);
            let len = response_bytes.len() as u32;
            res_buf.put_slice(&len.to_be_bytes());
            res_buf.put_slice(&response_bytes);

            let frame_len = res_buf.len();
            let frame = res_buf.split_to(frame_len).freeze();
            pool.put(res_buf);

            let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));
            Ok(http::Response::builder()
                .status(200)
                .header("content-type", "application/grpc")
                .body(res_body)
                .unwrap())
        })
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr: std::net::SocketAddr = "[::1]:50051".parse()?;
    let pool = Arc::new(BufferPool::new(1024));
    let hello = MyHelloWorld::new(pool.clone());

    println!("Server listening on {}", addr);

    Server::builder()
        .add_service(HelloWorldServer::new(hello, pool))
        .serve(addr)
        .await?;

    Ok(())
}
