//! Builds a Progressive Web App Manifest page.
use axum::http::header::CONTENT_TYPE;
use axum::http::HeaderValue;
use axum::response::{IntoResponse, Response};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;

use crate::https::extractors::DomainInfo;

/// The MIME type for `.webmanifest` files.
const MIME_TYPE_MANIFEST: &str = "application/manifest+json;charset=utf-8";

/// Create a new manifest builder.

#[skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Manifest {
    name: String,
    short_name: String,
    start_url: String,
    #[serde(rename = "display")]
    display_mode: DisplayMode,
    background_color: String,
    description: Option<String>,
    #[serde(rename = "dir")]
    direction: Direction,
    // direction: Option<Direction>,
    orientation: Option<String>,
    // orientation: Option<Orientation>,
    lang: Option<String>,
    scope: Option<String>,
    //
    theme_color: String,
    prefer_related_applications: Option<bool>,
    // #[serde(borrow)]
    //
    icons: Vec<ManifestIcon>,
    // icons: Vec<Icon<'i>>,
    // #[serde(borrow)]
    related_applications: Option<Vec<String>>,
    // related_applications: Vec<Related<'r>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
struct ManifestIcon {
    src: String,
    #[serde(rename = "type")]
    mime_type: String,
    sizes: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    purpose: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
enum Direction {
    /// left-to-right
    #[serde(rename = "ltr")]
    Ltr,
    /// right-to-left
    #[serde(rename = "rtl")]
    Rtl,
    /// Hints to the browser to use the [Unicode bidirectional
    /// algorithm](https://developer.mozilla.org/en-US/docs/Web/Localization/Unicode_Bidirectional_Text_Algorithm)
    /// to make a best guess about the text's direction.
    #[serde(rename = "auto")]
    Auto,
}

/// Display modes from the Web app manifest definition
///
/// Ref: <https://developer.mozilla.org/en-US/docs/Web/Manifest/display>
#[derive(Debug, Clone, Serialize, Deserialize)]
enum DisplayMode {
    /// All of the available display area is used and no user agent chrome is
    /// shown.
    #[serde(rename = "full-screen")]
    FullScreen,
    /// The application will look and feel like a standalone application. This can
    /// include the application having a different window, its own icon in the
    /// application launcher, etc. In this mode, the user agent will exclude UI
    /// elements for controlling navigation, but can include other UI elements
    /// such as a status bar.
    #[serde(rename = "standalone")]
    Standalone,
    /// The application will look and feel like a standalone application, but will
    /// have a minimal set of UI elements for controlling navigation. The elements
    /// will vary by browser.
    #[serde(rename = "minimal-ui")]
    MinimalUi,
    /// The application opens in a conventional browser tab or new window,
    /// depending on the browser and platform. This is the default.
    #[serde(rename = "browser")]
    Browser,
}

pub fn manifest_data(host_req: Option<&str>, domain_display_name: String) -> Manifest {
    let icons = vec![
        ManifestIcon {
            sizes: String::from("512x512"),
            src: String::from("/pkg/img/logo-square.svg"),
            mime_type: String::from("image/svg+xml"),
            purpose: None,
        },
        ManifestIcon {
            sizes: String::from("512x512"),
            src: String::from("/pkg/img/logo-512.png"),
            mime_type: String::from("image/png"),
            purpose: Some(String::from("maskable")),
        },
        ManifestIcon {
            sizes: String::from("192x192"),
            src: String::from("/pkg/img/logo-192.png"),
            mime_type: String::from("image/png"),
            purpose: Some(String::from("maskable")),
        },
        ManifestIcon {
            sizes: String::from("256x156"),
            src: String::from("/pkg/img/logo-256.png"),
            mime_type: String::from("image/png"),
            purpose: Some(String::from("maskable")),
        },
    ];

    let start_url = match host_req {
        Some(value) => format!("https://{value}/"),
        None => String::from("/"),
    };

    Manifest {
        short_name: "Kanidm".to_string(),
        name: domain_display_name,
        start_url,
        display_mode: DisplayMode::MinimalUi,
        description: None,
        orientation: None,
        lang: Some("en".to_string()),
        theme_color: "white".to_string(),
        background_color: "white".to_string(),
        direction: Direction::Auto,
        scope: None,
        prefer_related_applications: None,
        icons,
        related_applications: None,
    }
}

/// Generates a manifest.json file for progressive web app usage
pub(crate) async fn manifest(DomainInfo(domain_info): DomainInfo) -> impl IntoResponse {
    let domain_display_name = domain_info.display_name().to_string();
    // TODO: fix the None here to make it the request host
    let manifest_string =
        serde_json::to_string_pretty(&manifest_data(None, domain_display_name)).unwrap_or_default();
    let mut res = Response::new(manifest_string);

    res.headers_mut()
        .insert(CONTENT_TYPE, HeaderValue::from_static(MIME_TYPE_MANIFEST));

    res
}
