use crate::constants;
use actix_multipart::form::MultipartForm;
use actix_multipart::form::tempfile::TempFile;
use actix_web::dev::Payload;
use actix_web::{Error, FromRequest, HttpRequest};
use mime_guess::Mime;
use std::io::Read;
use std::str::FromStr;

#[derive(Debug, MultipartForm)]
struct FileUploadForm {
    #[multipart(rename = "file")]
    file: TempFile,
}

pub struct FileExtractor {
    pub buff: Vec<u8>,
    pub mime: Mime,
    pub name: Option<String>,
}

impl FileExtractor {
    pub fn name(&self) -> String {
        match &self.name {
            None => {
                let ext = self.mime.suffix().map(|s| s.as_str()).unwrap_or("bin");
                format!("file.{ext}")
            }
            Some(f) => f.to_string(),
        }
    }
}

impl FromRequest for FileExtractor {
    type Error = Error;
    type Future = futures_util::future::LocalBoxFuture<'static, Result<Self, Self::Error>>;

    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
        let form = MultipartForm::<FileUploadForm>::from_request(req, payload);

        Box::pin(async move {
            let mut form = form.await?;

            if form.file.size > constants::MAX_UPLOAD_FILE_SIZE {
                return Err(actix_web::error::ErrorPayloadTooLarge(
                    "Uploaded file is too large!",
                ));
            }

            let mut buff = Vec::with_capacity(form.file.size);
            form.file.file.read_to_end(&mut buff)?;

            let mime = match form
                .file
                .content_type
                .clone()
                .or_else(|| Mime::from_str(form.file.file_name.as_deref().unwrap_or("")).ok())
            {
                Some(s) => s,
                None => {
                    return Err(actix_web::error::ErrorBadRequest(
                        "Mimetype was not specified!!",
                    ));
                }
            };

            Ok(Self {
                mime,
                buff,
                name: form.file.file_name.clone(),
            })
        })
    }
}
