use actix_multipart::Multipart;
use actix_web::{web, HttpResponse, post, delete, get, ResponseError};
use log::error;
use serenity::futures::StreamExt;
use siren::ServiceError;

use crate::{auth::{JwtAuth, InsertUser, QueryUser}, storage::{upload_file, get_file, delete_file}};

#[post("/picture")]
async fn set_picture(mut payload: Multipart, auth: JwtAuth) -> HttpResponse {
  while let Some(item) = payload.next().await {
    let mut bytes = web::BytesMut::new();
    let mut field = match item {
      Ok(field) => field,
      Err(err) => return ResponseError::error_response(&err)
    };
    let content_type = field.content_disposition();
    // Get file name and construct the file path
    let file_name = match content_type.get_filename() {
      Some(name) => {
        // Verify extension is supported
        match name.split(".").last() {
          Some(ext) => {
            match ext {
              "png" | "jpg" | "jpeg"  => name,
              _ => return ResponseError::error_response(&ServiceError {
                status: 400,
                message: "File extension is not supported".to_string()
              })
            }
          },
          None => return ResponseError::error_response(&ServiceError {
            status: 400,
            message: "Unknown file extension".to_string()
          })
        }
      },
      None => return ResponseError::error_response(&ServiceError {
        status: 400,
        message: "File name is not provided".to_string()
      })
    };
    let path = format!("users/{}/{}", auth.user.email, file_name);

    // Build the file and store it in minio
    while let Some(chunk) = field.next().await {
      let data = match chunk {
        Ok(data) => data,
        Err(err) => {
          error!("Failed to get chunk: {}", err);
          return ResponseError::error_response(&err);
        }
      };
      bytes.extend_from_slice(&data);
    }
    match upload_file(&path, &bytes).await {
      Ok(_) => {
        match InsertUser::update_profile(&auth.user.email, Some(&path)) {
          Ok(_) => {}
          Err(err) => {
            error!("Failed to update user profile: {}", err);
            return ResponseError::error_response(&err);
          }
        };
      },
      Err(err) => {
        error!("Failed to upload file: {}", err);
        return ResponseError::error_response(&err);
      }
    }
  };
  return HttpResponse::Ok().finish();
}

#[get("/picture")]
async fn get_picture(auth: JwtAuth) -> HttpResponse {
  let user = match QueryUser::get_by_email(&auth.user.email) {
    Ok(user) => user,
    Err(err) => {
      error!("Failed to get user: {}", err);
      return ResponseError::error_response(&err);
    }
  };
  if let Some(path) = user.profile_picture {
    match get_file(&path).await {
      Ok(bytes) => return HttpResponse::Ok().body(bytes),
      Err(err) => {
        error!("Failed to get file: {}", err);
        return ResponseError::error_response(&err);
      }
    }
  } else {
    return HttpResponse::NotFound().finish();
  }
}

#[delete("/picture")]
async fn delete_picture(auth: JwtAuth) -> HttpResponse {
  match QueryUser::get_by_email(&auth.user.email) {
    Ok(user) => {
      match user.profile_picture {
        Some(path) => {
          match delete_file(&path).await {
            Ok(_) => {
              match InsertUser::update_profile(&auth.user.email, None) {
                Ok(_) => {}
                Err(err) => {
                  error!("Failed to update user profile: {}", err);
                  return ResponseError::error_response(&err);
                }
              };
            }
            Err(err) => {
              error!("Failed to delete file: {}", err);
              return ResponseError::error_response(&err);
            }
          };
        },
        None => {}
      }
    },
    Err(err) => {
      error!("Failed to get user: {}", err);
      return ResponseError::error_response(&err);
    }
  };
  return HttpResponse::Ok().finish();
}

pub fn init_routes(config: &mut web::ServiceConfig) {
  config.service(web::scope("users")
    .service(set_picture)
    .service(get_picture)
    .service(delete_picture)
  );
}