//! Route handler for serving raw file contents from a git repository.
//!
//! Covers one route: `/{repo}/commits/{sha}/raw/{*path}`.  The handler resolves
//! the rev-spec `{sha}:{path}` via `gix`, validates the path against directory
//! traversal, and serves the blob bytes with an appropriate `Content-Type`
//! header derived from the file extension.

use axum::extract::{Path, State};
use axum::http::header;
use axum::response::IntoResponse;
use serde::Deserialize;

use crate::error::{Error, Result};
use crate::git::{self, GitResultExt as _};
use crate::routes::{AppState, RepoName};

/// Path parameters for the raw file route.
#[derive(Deserialize)]
pub(super) struct RawPath {
    sha: String,
    path: String,
}

/// Serves the raw contents of a file at `{sha}:{path}` in the given repository
/// with an appropriate `Content-Type` header.
pub async fn handler(
    State(state): State<AppState>,
    RepoName(name): RepoName,
    Path(params): Path<RawPath>,
) -> Result<impl IntoResponse> {
    git::validate_path(&params.path)?;

    let git_repo = git::open_repo(&state.root, &name)?;

    // Build a `sha:path` rev-spec and resolve it to a blob.
    let spec = format!("{}:{}", params.sha, params.path);
    let blob = git_repo
        .rev_parse_single(spec.as_str())
        .map_err(|_| Error::NotFound(spec.clone()))?
        .object()
        .corrupt()?
        .try_into_blob()
        .map_err(|_| Error::NotFound(spec))?;

    let content_type = content_type_for(&params.path);

    Ok(([(header::CONTENT_TYPE, content_type)], blob.data.clone()))
}

/// Returns a `Content-Type` string based on the file extension of `path`.
/// Falls back to `application/octet-stream` for unknown extensions.
fn content_type_for(path: &str) -> &'static str {
    match path.rsplit('.').next().unwrap_or("") {
        "png" => "image/png",
        "jpg" | "jpeg" => "image/jpeg",
        "gif" => "image/gif",
        "svg" => "image/svg+xml",
        "webp" => "image/webp",
        "ico" => "image/x-icon",
        "txt" | "md" => "text/plain; charset=utf-8",
        _ => "application/octet-stream",
    }
}
