use deboa::{
    catcher::DeboaCatcher,
    errors::{DeboaError, IoError},
    fs::io::Decompressor,
    request::DeboaRequest,
    response::DeboaResponse,
    Result,
};
use http::{header, HeaderValue};
use std::collections::HashMap;

pub struct EncodingCatcher<D: Decompressor> {
    pub accept_encoding: HashMap<String, Box<D>>,
}

impl<D: Decompressor> EncodingCatcher<D> {
    pub fn register_decoders(decoders: Vec<D>) -> Self {
        let mut accept_encoding = HashMap::new();
        for decoder in decoders {
            accept_encoding.insert(decoder.name(), Box::new(decoder));
        }
        Self { accept_encoding }
    }
}

#[deboa::async_trait]
impl<D: Decompressor> DeboaCatcher for EncodingCatcher<D> {
    async fn on_request(&self, request: &mut DeboaRequest) -> Result<Option<DeboaResponse>> {
        let encodings = self
            .accept_encoding
            .values()
            .map(|decoder| decoder.name())
            .collect::<Vec<String>>();

        request
            .headers_mut()
            .insert(header::ACCEPT_ENCODING, HeaderValue::from_str(&encodings.join(",")).unwrap());

        Ok(None)
    }

    async fn on_response(&self, response: &mut DeboaResponse) -> Result<()> {
        let content_encoding = response
            .headers()
            .get(header::CONTENT_ENCODING);
        if content_encoding.is_none() {
            return Err(DeboaError::Io(IoError::Decompress {
                message: "Content encoding not found".to_string(),
            }));
        }

        let decompressor = self
            .accept_encoding
            .get(
                content_encoding
                    .unwrap()
                    .to_str()
                    .unwrap(),
            );
        if decompressor.is_none() {
            return Err(DeboaError::Io(IoError::Decompress {
                message: "No decompressor found".to_string(),
            }));
        }

        decompressor
            .unwrap()
            .decompress_body(response)
            .await?;
        Ok(())
    }
}
