// Copyright (C) 2026 Ordinary Labs, LLC.
//
// SPDX-License-Identifier: AGPL-3.0-only

use crate::server::{GMT_FORMAT, OrdinaryAppServerState};
use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::http::header;
use axum::response::{IntoResponse, Response};
use hyper::HeaderMap;
use hyper::http::HeaderValue;
use ordinary_config::CompressionAlgorithm;
use ordinary_utils::middleware::check_if_none_match;
use std::sync::Arc;
use time::{Duration, OffsetDateTime, UtcDateTime};

pub async fn get(
    State(state): State<Arc<OrdinaryAppServerState>>,
    Path(path): Path<String>,
    headers: HeaderMap,
) -> impl IntoResponse {
    get_asset_to_res(state, path, headers)
}

#[allow(clippy::needless_pass_by_value)]
pub fn get_asset_to_res(
    state: Arc<OrdinaryAppServerState>,
    path: String,
    headers: HeaderMap,
) -> impl IntoResponse {
    let span = tracing::info_span!("assets");

    span.in_scope(|| {
        let span = tracing::info_span!("storage");

        span.in_scope(|| {
            let no_compress = if let Some(ext) = path.split('.').next_back() {
                matches!(
                    ext,
                    "png"
                        | "apng"
                        | "gif"
                        | "jpg"
                        | "jpeg"
                        | "bmp"
                        | "tif"
                        | "tiff"
                        | "webp"
                        | "avif"
                        | "ico"
                        | "pdf"
                )
            } else {
                false
            };

            if !no_compress
                && let Some(assets_config) = &state.config.assets
                && let Some(precompression) = &assets_config.precompression
                && let Some(compressions) = headers.get(header::ACCEPT_ENCODING)
                && let Ok(compressions_str) = compressions.to_str()
            {
                for alg in precompression {
                    if compressions_str.contains(alg.as_str())
                        && let Some(value) =
                            get_asset_with_compression(&state, &path, &headers, Some(alg))
                    {
                        return value;
                    }
                }
            }

            if let Some(value) = get_asset_with_compression(&state, &path, &headers, None) {
                return value;
            }

            StatusCode::NOT_FOUND.into_response()
        })
    })
}

fn get_asset_with_compression(
    state: &Arc<OrdinaryAppServerState>,
    path: &str,
    headers: &HeaderMap,
    compression: Option<&CompressionAlgorithm>,
) -> Option<Response> {
    let span = tracing::info_span!("asset");

    span.in_scope(|| {
        if let Ok(asset) = state.storage.asset.get(path, compression)
            && let Ok(reader) = flexbuffers::Reader::get_root(asset.as_ref())
        {
            let vec = reader.as_vector();

            let etag = vec.idx(2).as_str();
            let last_modified = vec.idx(3).as_str();

            let mut header_map = HeaderMap::with_capacity(9);
            header_map.insert(
                header::VARY,
                HeaderValue::from_static(header::ACCEPT_ENCODING.as_str()),
            );

            if let Ok(etag) = HeaderValue::from_str(etag) {
                header_map.insert(header::ETAG, etag);
            }
            if let Ok(last_modified) = HeaderValue::from_str(last_modified) {
                header_map.insert(header::LAST_MODIFIED, last_modified);
            }
            if let Some(assets) = &state.config.assets {
                if let Some(http_cache_control) = &assets.internal_cache_control_header_value
                    && let Ok(cache_control) = HeaderValue::from_str(http_cache_control.as_str())
                {
                    header_map.insert(header::CACHE_CONTROL, cache_control);
                }

                if let Some(http_cache) = &assets.http
                    && let Some(expires_s) = http_cache.expires
                {
                    let future = UtcDateTime::now() + Duration::seconds(expires_s.cast_signed());

                    if let Ok(formatted) = future.format(&GMT_FORMAT)
                        && let Ok(expires) = HeaderValue::from_str(formatted.as_str())
                    {
                        header_map.insert(header::EXPIRES, expires);
                    }
                }
            }

            if let Some(etag) = check_if_none_match(headers, etag)
                && let Ok(etag_header) = HeaderValue::from_str(etag)
            {
                header_map.insert(header::ETAG, etag_header);

                return Some((StatusCode::NOT_MODIFIED, header_map).into_response());
            } else if let Some(if_modified_since) = headers.get(header::IF_MODIFIED_SINCE)
                && let Ok(if_modified_since_str) = if_modified_since.to_str()
                && let Ok(if_modified_since) =
                    OffsetDateTime::parse(if_modified_since_str, &GMT_FORMAT)
                && let Ok(last_modified) = OffsetDateTime::parse(last_modified, &GMT_FORMAT)
                && if_modified_since >= last_modified
            {
                return Some((StatusCode::NOT_MODIFIED, header_map).into_response());
            }

            let mime = vec.idx(0).as_str();

            if let Ok(mime) = HeaderValue::from_str(mime) {
                header_map.insert(header::CONTENT_TYPE, mime);
            }

            if let Some(compression) = compression {
                header_map.insert(
                    header::CONTENT_ENCODING,
                    HeaderValue::from_static(compression.as_str()),
                );
            }

            return Some(
                (
                    StatusCode::OK,
                    header_map,
                    bytes::Bytes::copy_from_slice(vec.idx(1).as_blob().0),
                )
                    .into_response(),
            );
        }

        None
    })
}
