use crate::connections::s3_connection;
use crate::controllers::HttpResult;
use crate::models::PhotoID;
use crate::services::photos_service;
use actix_web::http::header;
use actix_web::{HttpRequest, HttpResponse, web};
use std::ops::Add;
use std::time::{Duration, UNIX_EPOCH};

#[derive(serde::Deserialize)]
pub struct PhotoIdPath {
    id: String,
}

pub async fn get_full_size(id: web::Path<PhotoIdPath>, req: HttpRequest) -> HttpResult {
    get_photo(&id, true, req).await
}

pub async fn get_thumbnail(id: web::Path<PhotoIdPath>, req: HttpRequest) -> HttpResult {
    get_photo(&id, false, req).await
}

async fn get_photo(id: &PhotoIdPath, full_size: bool, req: HttpRequest) -> HttpResult {
    let id = match PhotoID::from_signed_hash(&id.id) {
        None => {
            return Ok(HttpResponse::Unauthorized().body("Invalid hash"));
        }
        Some(p) => p,
    };

    let photo = photos_service::get_by_id(id).await?;

    let (hash, content_type) = match full_size {
        true => (photo.sha512.as_str(), photo.mime_type.as_str()),
        false => (photo.thumb_sha512.as_str(), "application/png"),
    };

    // Check if an upload is un-necessary
    if let Some(c) = req.headers().get(header::IF_NONE_MATCH)
        && c.to_str().unwrap_or("") == hash {
            return Ok(HttpResponse::NotModified().finish());
        }

    if let Some(c) = req.headers().get(header::IF_MODIFIED_SINCE) {
        let date_str = c.to_str().unwrap_or("");
        if let Ok(date) = httpdate::parse_http_date(date_str)
            && date
                .add(Duration::from_secs(1))
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs()
                >= photo.time_create as u64
            {
                return Ok(HttpResponse::NotModified().finish());
            }
    }

    let bytes = s3_connection::get_file(&match full_size {
        true => photo.photo_path(),
        false => photo.thumbnail_path(),
    })
    .await?;

    Ok(HttpResponse::Ok()
        .content_type(content_type)
        .insert_header(("etag", hash))
        .insert_header((
            "last-modified",
            httpdate::fmt_http_date(UNIX_EPOCH + Duration::from_secs(photo.time_create as u64)),
        ))
        .body(bytes))
}
