//! Blob handlers — chunked file upload/download.

use std::sync::Arc;

use bytes::Bytes;
use prost::Message;
use quicproquo_proto::qpq::v1;
use quicproquo_rpc::method::{HandlerResult, RequestContext};

use crate::domain::blobs::BlobService;
use crate::domain::types::{CallerAuth, DownloadBlobReq, UploadBlobReq};

use super::{domain_err, require_auth, ServerState};

fn caller_auth(identity_key: Vec<u8>) -> CallerAuth {
    CallerAuth {
        identity_key,
        token: Vec::new(),
        device_id: None,
    }
}

pub async fn handle_upload_blob(state: Arc<ServerState>, ctx: RequestContext) -> HandlerResult {
    let identity_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

    let req = match v1::UploadBlobRequest::decode(ctx.payload) {
        Ok(r) => r,
        Err(e) => {
            return HandlerResult::err(
                quicproquo_rpc::error::RpcStatus::BadRequest,
                &format!("decode: {e}"),
            )
        }
    };

    let svc = BlobService {
        data_dir: state.data_dir.clone(),
    };
    let auth = caller_auth(identity_key);

    let domain_req = UploadBlobReq {
        blob_hash: req.blob_hash,
        chunk: req.chunk,
        offset: req.offset,
        total_size: req.total_size,
        mime_type: req.mime_type,
    };

    match svc.upload_blob(domain_req, &auth) {
        Ok(resp) => {
            let proto = v1::UploadBlobResponse {
                blob_id: resp.blob_id,
            };
            HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
        }
        Err(e) => domain_err(e),
    }
}

pub async fn handle_download_blob(state: Arc<ServerState>, ctx: RequestContext) -> HandlerResult {
    let identity_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

    let req = match v1::DownloadBlobRequest::decode(ctx.payload) {
        Ok(r) => r,
        Err(e) => {
            return HandlerResult::err(
                quicproquo_rpc::error::RpcStatus::BadRequest,
                &format!("decode: {e}"),
            )
        }
    };

    let svc = BlobService {
        data_dir: state.data_dir.clone(),
    };
    let auth = caller_auth(identity_key);

    let domain_req = DownloadBlobReq {
        blob_id: req.blob_id,
        offset: req.offset,
        length: req.length,
    };

    match svc.download_blob(domain_req, &auth) {
        Ok(resp) => {
            let proto = v1::DownloadBlobResponse {
                chunk: resp.chunk,
                total_size: resp.total_size,
                mime_type: resp.mime_type,
            };
            HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
        }
        Err(e) => domain_err(e),
    }
}
