Documentation
use super::ExtractError;
use http::header;
use mincat_core::request::{FromRequest, Request, RequestExt};
use multer::{parse_boundary, Field};

use multer_derive::{FromMultipart, MultipartForm};

pub trait FromMultipartNull {
    fn is_null() -> bool;
    fn generate(multipart: multer::Multipart<'static>) -> Self;
}

pub struct Null(multer::Multipart<'static>);

impl FromMultipart for Null {
    fn from_multipart(
        _: &multer_derive::MultipartForm,
        _: multer_derive::FormContext<'_>,
    ) -> Result<Self, multer_derive::Error> {
        todo!()
    }
}

impl FromMultipartNull for Null {
    fn is_null() -> bool {
        true
    }

    fn generate(multipart: multer::Multipart<'static>) -> Self {
        Self(multipart)
    }
}

pub struct FormData<T = Null>(pub T)
where
    T: FromMultipart + FromMultipartNull;

#[async_trait::async_trait]
impl<T> FromRequest for FormData<T>
where
    T: FromMultipart + FromMultipartNull + 'static,
{
    type Error = ExtractError;

    async fn from_request(request: Request) -> Result<Self, Self::Error> {
        let boundary = parse_boundary(
            request
                .headers()
                .get(header::CONTENT_TYPE)
                .ok_or(ExtractError("missing content type".to_string()))?
                .to_str()
                .ok()
                .ok_or(ExtractError("content type ".to_string()))?,
        )
        .map_err(ExtractError::from)?;

        let body = request.change_to_limited_body().into_body();

        if T::is_null() {
            let multipart = multer::Multipart::new(body, boundary);
            let res = T::generate(multipart);
            Ok(FormData(res))
        } else {
            let multipart = multer_derive::multer::Multipart::new(body, boundary);
            let form = MultipartForm::with_multipart(multipart)
                .await
                .map_err(ExtractError::from)?;
            let res = T::from_multipart(&form, Default::default()).map_err(ExtractError::from)?;
            Ok(FormData(res))
        }
    }
}

impl FormData<Null> {
    pub async fn next_field(&mut self) -> Result<Option<Field<'static>>, ExtractError> {
        self.0 .0.next_field().await.map_err(ExtractError::from)
    }
}