use crate::google::protobuf::descriptor::{ServiceDescriptorProto, MethodDescriptorProto};
use crate::google::protobuf::descriptor::FileDescriptorSet;
use crate::generator::generate_files_common;
use crate::generator::SERVICE_IMPORTS;

use crate::generator::utils::{to_pascal_case, to_snake_case};

pub fn generate_service_code(

    set: &FileDescriptorSet,
    files_to_generate: Option<&[String]>,
    generate_mod_files: bool,
) -> Vec<(String, String)> {
    generate_files_common(
        set,
        files_to_generate,
        generate_mod_files,
        "",
        |file_proto, output| {
            let package = file_proto.package().unwrap_or("").to_string();
            // Services
            for svc_res in file_proto.service() {
                let (svc_data, _) = svc_res.expect("Failed to iterate service");
                write_service(
                    &ServiceDescriptorProto::new(svc_data)
                        .expect("Failed to parse ServiceDescriptorProto"),
                    &package,
                    output,
                );
            }
        },
    )
}
pub fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut String) {
    output.push_str(SERVICE_IMPORTS);
    output.push_str("\n");
    let svc_name = to_pascal_case(svc_proto.name().unwrap());
    
    output.push_str(&format!(
        "#[async_trait::async_trait]\npub trait {}: Send + Sync + 'static {{\n",
        svc_name
    ));

    for method_res in svc_proto.method() {
        let (method_data, _) = method_res.expect("Failed to iterate method");
        let method_proto =
            MethodDescriptorProto::new(method_data).expect("Failed to parse MethodDescriptorProto");
        let method_name = to_snake_case(method_proto.name().unwrap());

        let input_full_name = method_proto.input_type().unwrap();
        let output_full_name = method_proto.output_type().unwrap();

        let input_type = input_full_name.split('.').last().unwrap();
        let output_type = output_full_name.split('.').last().unwrap();

        let input_owned = format!("Owned{}", input_type);
        let output_owned = format!("Owned{}", output_type);

        let client_streaming = method_proto.client_streaming().unwrap_or(false);
        let server_streaming = method_proto.server_streaming().unwrap_or(false);

        let req_type = if client_streaming {
            format!("Request<tonic::Streaming<{}>>", input_owned)
        } else {
            format!("Request<{}>", input_owned)
        };

        let resp_type = if server_streaming {
            format!(
                "Response<Pin<Box<dyn Stream<Item = std::result::Result<{}, Status>> + Send>>>",
                output_owned
            )
        } else {
            format!("Response<{}>", output_owned)
        };

        output.push_str(&format!(
            "    async fn {}(&self, request: {}) -> std::result::Result<{}, Status>;\n",
            method_name, req_type, resp_type
        ));
    }
    output.push_str("}\n\n");

    let server_name = format!("{}Server", svc_name);
    
    output.push_str(&format!(
        "#[derive(Clone)]\npub struct {} {{\n",
        server_name
    ));
    output.push_str(&format!("    inner: Arc<dyn {}>,\n", svc_name));
    output.push_str("    pool: Arc<BufferPool>,\n");
    output.push_str("}\n\n");

    
    output.push_str(&format!("impl {} {{\n", server_name));
    output.push_str(&format!(
        "    pub fn new(inner: Arc<dyn {}>, pool: Arc<BufferPool>) -> Self {{\n",
        svc_name
    ));
    output.push_str("        Self { inner, pool }\n");
    output.push_str("    }\n");
    output.push_str("}\n\n");

    
    output.push_str(&format!(
        "impl tonic::server::NamedService for {} {{\n",
        server_name
    ));
    let full_svc_name = if package.is_empty() {
        svc_proto.name().unwrap().to_string()
    } else {
        format!("{}.{}", package, svc_proto.name().unwrap())
    };
    output.push_str(&format!(
        "    const NAME: &'static str = \"{}\";\n",
        full_svc_name
    ));
    output.push_str("}\n\n");

    
    output.push_str(&format!(
        "impl Service<http::Request<BoxBody>> for {} {{\n",
        server_name
    ));
    output.push_str("    type Response = http::Response<BoxBody>;\n");
    output.push_str("    type Error = std::convert::Infallible;\n");
    output.push_str("    type Future = Pin<Box<dyn Future<Output = std::result::Result<Self::Response, Self::Error>> + Send>>;\n\n");

    output.push_str("    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {\n");
    output.push_str("        Poll::Ready(Ok(()))\n");
    output.push_str("    }\n\n");

    output.push_str("    fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {\n");
    output.push_str("        let inner = self.inner.clone();\n");
    output.push_str("        let pool = self.pool.clone();\n");
    output.push_str("        Box::pin(async move {\n");
    output.push_str("            let path = req.uri().path().to_string();\n");
    output.push_str("            let body = req.into_body();\n");
    output.push_str("            let mut buf = pool.get();\n");
    output.push_str("            let mut stream = body;\n");
    output.push_str("            while let Some(frame_result) = stream.frame().await {\n");
    output.push_str("                let frame = frame_result.expect(\"Body frame error\");\n");
    output.push_str("                if let Some(data) = frame.data_ref() {\n");
    output.push_str("                    buf.put(data.clone());\n");
    output.push_str("                }\n");
    output.push_str("            }\n\n");

    output.push_str("            let total_len = buf.len();\n");
    output.push_str("            let bytes_vec = buf.split_to(total_len).freeze();\n");
    output.push_str("            pool.put(buf);\n");
    output.push_str("            if bytes_vec.len() < 5 {\n");
    output.push_str("                let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
    output.push_str("                return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
    output.push_str("            }\n\n");
    output.push_str("            let payload = bytes_vec.slice(5..);\n");
    output.push_str("            let mut routed = false;\n\n");

    let mut methods = Vec::new();
    for method_res in svc_proto.method() {
        let (method_data, _) = method_res.expect("Failed to iterate method");
        let method_proto =
            MethodDescriptorProto::new(method_data).expect("Failed to parse MethodDescriptorProto");
        let original_method_name = method_proto.name().unwrap().to_string();
        let method_name = to_snake_case(&original_method_name);
        let input_full_name = method_proto.input_type().unwrap();
        let input_type = input_full_name.split('.').last().unwrap();
        let input_owned = format!("Owned{}", input_type);
        let server_streaming = method_proto.server_streaming().unwrap_or(false);
        methods.push((
            original_method_name,
            method_name,
            input_owned,
            server_streaming,
        ));
    }

    for (original_method_name, method_name, input_owned, server_streaming) in methods {
        if server_streaming {
            // For streaming RPCs, we don't implement the server logic yet.
            // We just make it compile by returning a "not implemented" response.
            let full_path = if package.is_empty() {
                format!("/{}/{}", svc_proto.name().unwrap(), original_method_name)
            } else {
                format!(
                    "/{}.{}/{}",
                    package,
                    svc_proto.name().unwrap(),
                    original_method_name
                )
            };
            output.push_str(&format!("            if path == \"{}\" {{\n", full_path));
            output.push_str("                let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
            output.push_str("                return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
            output.push_str("            }\n");
            continue;
        }
        let full_path = if package.is_empty() {
            format!("/{}/{}", svc_proto.name().unwrap(), original_method_name)
        } else {
            format!(
                "/{}.{}/{}",
                package,
                svc_proto.name().unwrap(),
                original_method_name
            )
        };
        output.push_str(&format!("            if path == \"{}\" {{\n", full_path));
        output.push_str(&format!(
            "                let request_msg = match {}::decode(payload) {{\n",
            input_owned
        ));
        output.push_str("                    Ok(msg) => msg,\n");
        output.push_str("                    Err(e) => {\n");
        output.push_str("                        let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
        output.push_str("                        return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
        output.push_str("                    }\n");
        output.push_str("                };\n\n");
        output.push_str(&format!(
            "                let response = match inner.{}(Request::new(request_msg)).await {{\n",
            method_name
        ));
        output.push_str("                    Ok(res) => res,\n");
        output.push_str("                    Err(e) => {\n");
        output.push_str("                        let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
        output.push_str("                        return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
        output.push_str("                    }\n");
        output.push_str("                };\n\n");
        output.push_str("                let response_msg = response.into_inner();\n");
        output.push_str("                let response_bytes = response_msg.bytes();\n");
        output.push_str("                let mut res_buf = pool.get();\n");
        output.push_str("                res_buf.put_u8(0);\n");
        output.push_str("                let len = response_bytes.len() as u32;\n");
        output.push_str("                res_buf.put_slice(&len.to_be_bytes());\n");
        output.push_str("                res_buf.put_slice(&response_bytes);\n");
        output.push_str("                let frame_len = res_buf.len();\n");
        output.push_str("                let frame = res_buf.split_to(frame_len).freeze();\n");
        output.push_str("                pool.put(res_buf);\n");
        output.push_str(
            "                let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));\n",
        );
        output.push_str("                routed = true;\n");
        output.push_str("                return Ok(http::Response::builder().status(200).header(\"content-type\", \"application/grpc\").body(res_body).unwrap());\n");
        output.push_str("            }\n");
    }

    output.push_str("            if !routed {\n");
    output.push_str("                let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
    output.push_str("                return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
    output.push_str("            }\n");
    output.push_str("            Ok(http::Response::builder().status(200).body(BoxBody::new(StatusBody::new(None, 0))).unwrap())\n");
    output.push_str("        })\n");
    output.push_str("    }\n");
    output.push_str("}\n");
}




