#[cfg(feature = "download")]
use crate::download::{ContentDisposition, DispositionType};
use crate::{body::HttpBody, error::Error, Result};
#[cfg(feature = "cookie")]
use cookie::CookieJar;
use headers::{Header, HeaderMapExt};
use hyper::{
    body::Bytes,
    http::{self, HeaderMap, HeaderName, HeaderValue},
    StatusCode,
};
use serde::Serialize;
use std::ops::{Deref, DerefMut};
#[derive(Debug, Default)]
pub struct Response {
    #[doc(hidden)]
    inner: hyper::Response<HttpBody>,
    #[doc(hidden)]
    status_set: bool,
    #[doc(hidden)]
    #[cfg(feature = "cookie")]
    cookies: CookieJar,
}
impl Response {
    #[inline]
    pub fn new(
        inner: hyper::Response<HttpBody>,
        status_set: bool,
        #[cfg(feature = "cookie")] cookies: CookieJar,
    ) -> Self {
        Response {
            inner,
            status_set,
            #[cfg(feature = "cookie")]
            cookies,
        }
    }
    #[cfg(feature = "cookie")]
    #[inline]
    pub fn cookie(&mut self, cookie: cookie::Cookie<'static>) -> &mut Self {
        self.cookies.add(cookie);
        self
    }
    #[cfg(feature = "cookie")]
    #[inline]
    pub fn cookies(&self) -> &CookieJar {
        &self.cookies
    }
    #[cfg(feature = "cookie")]
    #[inline]
    pub fn cookies_mut(&mut self) -> &mut CookieJar {
        &mut self.cookies
    }
    #[cfg(feature = "cookie")]
    #[inline]
    pub fn cookie_remove(&mut self, name: &str) -> &mut Self {
        if let Some(cookie) = self.cookies.get(name).cloned() {
            self.cookies.remove(cookie);
        }
        self
    }
    #[inline]
    pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
    where
        HeaderName: TryFrom<K>,
        <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
        HeaderValue: TryFrom<V>,
        <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
    {
        let name = <HeaderName as TryFrom<K>>::try_from(key)
            .map_err(Into::into)
            .expect("Invalid key");
        let value = <HeaderValue as TryFrom<V>>::try_from(value)
            .map_err(Into::into)
            .expect("Invalid value");
        self.inner.headers_mut().append(name, value);
        self
    }
    #[inline]
    pub fn header_set<H: Header>(&mut self, h: H) -> &mut Self {
        self.inner.headers_mut().typed_insert(h);
        self
    }
    #[inline]
    pub(crate) fn status_if_not_set<T>(&mut self, status: T) -> &mut Self
    where
        StatusCode: TryFrom<T>,
        <StatusCode as TryFrom<T>>::Error: Into<http::Error>,
    {
        if !self.status_set {
            self.status(status)
        } else {
            self
        }
    }
    #[inline]
    pub fn status<T>(&mut self, status: T) -> &mut Self
    where
        StatusCode: TryFrom<T>,
        <StatusCode as TryFrom<T>>::Error: Into<http::Error>,
    {
        self.status_set = true;
        *self.inner.status_mut() = TryFrom::try_from(status)
            .map_err(Into::into)
            .expect("error");
        self
    }
    #[inline]
    pub fn redirect<T>(&mut self, status: T, url: &str) -> &mut Self
    where
        StatusCode: TryFrom<T>,
        <StatusCode as TryFrom<T>>::Error: Into<http::Error>,
    {
        let value = http::header::HeaderValue::try_from(url).expect("url is not the correct value");
        self.status_set = true;
        *self.inner.status_mut() = TryFrom::try_from(status)
            .map_err(Into::into)
            .expect("error");
        self.inner
            .headers_mut()
            .append(http::header::LOCATION, value);
        self
    }
    #[inline]
    pub fn location(&mut self, location: &str) -> &mut Self {
        let value = http::header::HeaderValue::try_from(location)
            .expect("location is not the correct value");
        self.inner
            .headers_mut()
            .append(http::header::CONTENT_LOCATION, value);
        self
    }
    #[inline]
    pub fn body(&mut self, data: impl Into<Bytes>) -> &mut Self {
        let body: Bytes = data.into();
        let body: HttpBody = body.into();
        *self.inner.body_mut() = body;
        self
    }
    #[inline]
    pub fn content_type(&mut self, content_type: &str) -> &mut Self {
        if let Ok(c_type) = HeaderValue::from_str(content_type) {
            self.inner.headers_mut().insert("content-type", c_type);
        }
        self
    }
    #[inline]
    pub fn html<T: Into<Bytes>>(&mut self, value: T) -> &mut Self {
        self.content_type("text/html; charset=utf-8").body(value)
    }
    #[inline]
    pub fn text<T: Into<Bytes>>(&mut self, value: T) -> &mut Self {
        self.content_type("text/plain; charset=utf-8").body(value)
    }
    #[inline]
    pub fn json<T: Serialize>(&mut self, value: &T) -> &mut Self {
        match serde_json::to_vec(value) {
            Ok(v) => self.content_type("application/json").body(v),
            Err(e) => self
                .status_if_not_set(StatusCode::INTERNAL_SERVER_ERROR)
                .text(e.to_string()),
        }
    }
    #[inline]
    pub fn form<T: Serialize>(&mut self, value: &T) -> &mut Self {
        match serde_urlencoded::to_string(value) {
            Ok(v) => self
                .content_type("application/x-www-form-urlencoded")
                .body(v),
            Err(e) => self
                .status_if_not_set(StatusCode::INTERNAL_SERVER_ERROR)
                .text(e.to_string()),
        }
    }
    #[inline]
    pub fn stream<S, O, E>(&mut self, stream: S) -> &mut Self
    where
        S: futures::Stream<Item = Result<O, E>> + Send + Sync + 'static,
        O: Into<Bytes> + 'static,
        E: Into<Error> + 'static,
    {
        *self.inner.body_mut() = HttpBody::stream(stream);
        self
    }
    #[inline]
    pub async fn send_file<'a, P>(&'a mut self, path: P, req_headers: &'a HeaderMap) -> &'a mut Self
    where
        P: Into<std::path::PathBuf> + Send,
    {
        let path = path.into();
        if !path.exists() {
            self.status(StatusCode::NOT_FOUND)
        } else {
            match crate::fs::NamedFile::builder(path).build().await {
                Ok(file) => file.send(req_headers, self).await,
                Err(e) => self
                    .status(StatusCode::INTERNAL_SERVER_ERROR)
                    .text(e.to_string()),
            }
        }
    }
    #[cfg(feature = "download")]
    #[inline]
    pub fn write_file(
        &mut self,
        path: impl AsRef<std::path::Path>,
        disposition_type: DispositionType,
    ) -> Result<&mut Self, Error> {
        let path = path.as_ref();
        let mut file = std::fs::File::open(path)?;
        let mut buffer = Vec::new();
        use std::io::Read;
        file.read_to_end(&mut buffer)?;
        if let Some(filename) = path.file_name() {
            let name = filename.to_string_lossy();
            let content_disposition =
                ContentDisposition::new(disposition_type, Some(&name)).try_into()?;
            self.inner
                .headers_mut()
                .insert("content-disposition", content_disposition);
        }
        let body: Bytes = buffer.into();
        let body: HttpBody = body.into();
        *self.inner.body_mut() = body;
        Ok(self)
    }
    #[allow(unused_mut)]
    #[inline]
    pub fn into_raw(self) -> Result<hyper::Response<HttpBody>, Error> {
        let Response {
            mut inner,
            #[cfg(feature = "cookie")]
            cookies,
            ..
        } = self;
        #[cfg(feature = "cookie")]
        for c in cookies.iter() {
            inner.headers_mut().append(
                http::header::SET_COOKIE,
                http::HeaderValue::from_str(c.to_string().as_str())?,
            );
        }
        Ok(inner)
    }
    #[inline]
    pub fn map<F>(self, f: F) -> Response
    where
        F: FnOnce(HttpBody) -> HttpBody,
    {
        let inner = self.inner.map(f);
        Response {
            inner,
            status_set: self.status_set,
            #[cfg(feature = "cookie")]
            cookies: self.cookies,
        }
    }
}
impl Deref for Response {
    type Target = hyper::Response<HttpBody>;
    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}
impl DerefMut for Response {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}