use std::collections::HashMap;
use handlebars::Handlebars;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize)]
pub struct AltairSource<'a> {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    title: Option<&'a str>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    options: Option<serde_json::Value>,
}
impl<'a> AltairSource<'a> {
    pub fn build() -> AltairSource<'a> {
        Default::default()
    }
    pub fn title(self, title: &'a str) -> AltairSource<'a> {
        AltairSource {
            title: Some(title),
            ..self
        }
    }
    pub fn options<T: Serialize>(self, options: T) -> AltairSource<'a> {
        AltairSource {
            options: Some(serde_json::to_value(options).expect("Failed to serialize options")),
            ..self
        }
    }
    pub fn finish(self) -> String {
        let mut handlebars = Handlebars::new();
        handlebars.register_helper("toJson", Box::new(ToJsonHelper));
        handlebars
            .register_template_string("altair_source", include_str!("./altair_source.hbs"))
            .expect("Failed to register template");
        handlebars
            .render("altair_source", &self)
            .expect("Failed to render template")
    }
}
#[derive(Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct AltairWindowOptions {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub initial_name: Option<String>,
    #[serde(
        rename = "endpointURL",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub endpoint_url: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub subscriptions_endpoint: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub subscriptions_protocol: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub initial_query: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub initial_variables: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub initial_pre_request_script: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub initial_post_request_script: Option<String>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub initial_headers: HashMap<String, String>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub initial_subscriptions_payload: HashMap<String, String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub initial_http_method: Option<AltairHttpVerb>,
}
#[derive(Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct AltairConfigOptions {
    #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
    pub window_options: Option<AltairWindowOptions>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub initial_environments: Option<AltairInitialEnvironments>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub instance_storage_namespace: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub initial_settings: Option<AltairSettingsState>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub preserve_state: Option<bool>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub initial_windows: Vec<AltairWindowOptions>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub persisted_settings: Option<AltairSettingsState>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub disable_account: Option<bool>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[allow(missing_docs)]
pub enum AltairHttpVerb {
    POST,
    GET,
    PUT,
    DELETE,
}
#[derive(Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct AltairInitialEnvironments {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub base: Option<AltairInitialEnvironmentState>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub sub_environments: Vec<AltairInitialEnvironmentState>,
}
#[derive(Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct AltairInitialEnvironmentState {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub variables: HashMap<String, String>,
}
#[derive(Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct AltairSettingsState {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub theme: Option<String>,
    #[serde(
        rename = "theme.dark",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub theme_dark: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub language: Option<AltairSettingsLanguage>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub add_query_depth_limit: Option<usize>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tab_size: Option<usize>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub enable_experimental: Option<bool>,
    #[serde(
        rename = "theme.fontsize",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub theme_font_size: Option<usize>,
    #[serde(
        rename = "theme.editorFontFamily",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub theme_editor_font_family: Option<String>,
    #[serde(
        rename = "theme.editorFontSize",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub theme_editor_font_size: Option<usize>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub disable_push_notification: Option<bool>,
    #[serde(rename = "plugin.list", default, skip_serializing_if = "Vec::is_empty")]
    pub plugin_list: Vec<String>,
    #[serde(
        rename = "request.withCredentials",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub request_with_credentials: Option<bool>,
    #[serde(
        rename = "schema.reloadOnStart",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub schema_reload_on_start: Option<bool>,
    #[serde(
        rename = "alert.disableUpdateNotification",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub alert_disable_update_notification: Option<bool>,
    #[serde(
        rename = "alert.disableWarnings",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub alert_disable_warnings: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub history_depth: Option<usize>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub disable_line_numbers: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub theme_config: Option<serde_json::Value>,
    #[serde(
        rename = "themeConfig.dark",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub theme_config_dark: Option<serde_json::Value>,
    #[serde(
        rename = "response.hideExtensions",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub response_hide_extensions: Option<bool>,
    #[serde(
        rename = "editor.shortcuts",
        default,
        skip_serializing_if = "HashMap::is_empty"
    )]
    pub editor_shortcuts: HashMap<String, String>,
    #[serde(
        rename = "beta.disable.newEditor",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub beta_disable_new_editor: Option<bool>,
    #[serde(
        rename = "beta.disable.newScript",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub beta_disable_new_script: Option<bool>,
    #[serde(
        rename = "script.allowedCookies",
        default,
        skip_serializing_if = "Vec::is_empty"
    )]
    pub script_allowed_cookies: Vec<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub enable_tablist_scrollbar: Option<bool>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[allow(missing_docs)]
pub enum AltairSettingsLanguage {
    #[serde(rename = "en-US")]
    English,
    #[serde(rename = "fr-FR")]
    French,
    #[serde(rename = "es-ES")]
    EspaƱol,
    #[serde(rename = "cs-CZ")]
    Czech,
    #[serde(rename = "de-DE")]
    German,
    #[serde(rename = "pt-BR")]
    Brazilian,
    #[serde(rename = "ru-RU")]
    Russian,
    #[serde(rename = "uk-UA")]
    Ukrainian,
    #[serde(rename = "zh-CN")]
    ChineseSimplified,
    #[serde(rename = "ja-JP")]
    Japanese,
    #[serde(rename = "sr-SP")]
    Serbian,
    #[serde(rename = "it-IT")]
    Italian,
    #[serde(rename = "pl-PL")]
    Polish,
    #[serde(rename = "ko-KR")]
    Korean,
    #[serde(rename = "ro-RO")]
    Romanian,
    #[serde(rename = "vi-VN")]
    Vietnamese,
}
struct ToJsonHelper;
impl handlebars::HelperDef for ToJsonHelper {
    #[allow(unused_assignments)]
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &handlebars::Helper<'rc>,
        r: &'reg handlebars::Handlebars<'reg>,
        _: &'rc handlebars::Context,
        _: &mut handlebars::RenderContext<'reg, 'rc>,
    ) -> std::result::Result<handlebars::ScopedJson<'rc>, handlebars::RenderError> {
        let mut param_idx = 0;
        let obj = h
            .param(param_idx)
            .and_then(|x| {
                if r.strict_mode() && x.is_value_missing() {
                    None
                } else {
                    Some(x.value())
                }
            })
            .ok_or_else(|| {
                handlebars::RenderErrorReason::ParamNotFoundForName("toJson", "obj".to_string())
            })
            .and_then(|x| {
                x.as_object().ok_or_else(|| {
                    handlebars::RenderErrorReason::ParamTypeMismatchForName(
                        "toJson",
                        "obj".to_string(),
                        "object".to_string(),
                    )
                })
            })?;
        param_idx += 1;
        let result = if obj.is_empty() {
            "{}".to_owned()
        } else {
            serde_json::to_string(&obj).expect("Failed to serialize json")
        };
        Ok(handlebars::ScopedJson::Derived(
            handlebars::JsonValue::from(result),
        ))
    }
}
#[cfg(test)]
mod tests {
    use serde_json::json;
    use super::*;
    #[test]
    fn test_without_options() {
        let altair_source = AltairSource::build().title("Custom Title").finish();
        assert_eq!(
            altair_source,
            r#"<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Custom Title</title>
    <base href="https://unpkg.com/altair-static@latest/build/dist/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        AltairGraphQL.init();
      });
    </script>
    <app-root>
      <style>
        .loading-screen {
          /*Prevents the loading screen from showing until CSS is downloaded*/
          display: none;
        }
      </style>
      <div class="loading-screen styled">
        <div class="loading-screen-inner">
          <div class="loading-screen-logo-container">
            <img src="assets/img/logo_350.svg" alt="Altair">
          </div>
          <div class="loading-screen-loading-indicator">
            <span class="loading-indicator-dot"></span>
            <span class="loading-indicator-dot"></span>
            <span class="loading-indicator-dot"></span>
          </div>
        </div>
      </div>
    </app-root>
    <script type="text/javascript" src="runtime.js"></script>
    <script type="text/javascript" src="polyfills.js"></script>
    <script type="text/javascript" src="main.js"></script>
  </body>
</html>"#
        )
    }
    #[test]
    fn test_with_dynamic() {
        let altair_source = AltairSource::build()
            .options(json!({
                "endpointURL": "/",
                "subscriptionsEndpoint": "/ws",
            }))
            .finish();
        assert_eq!(
            altair_source,
            r#"<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Altair</title>
    <base href="https://unpkg.com/altair-static@latest/build/dist/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        AltairGraphQL.init({"endpointURL":"/","subscriptionsEndpoint":"/ws"});
      });
    </script>
    <app-root>
      <style>
        .loading-screen {
          /*Prevents the loading screen from showing until CSS is downloaded*/
          display: none;
        }
      </style>
      <div class="loading-screen styled">
        <div class="loading-screen-inner">
          <div class="loading-screen-logo-container">
            <img src="assets/img/logo_350.svg" alt="Altair">
          </div>
          <div class="loading-screen-loading-indicator">
            <span class="loading-indicator-dot"></span>
            <span class="loading-indicator-dot"></span>
            <span class="loading-indicator-dot"></span>
          </div>
        </div>
      </div>
    </app-root>
    <script type="text/javascript" src="runtime.js"></script>
    <script type="text/javascript" src="polyfills.js"></script>
    <script type="text/javascript" src="main.js"></script>
  </body>
</html>"#
        )
    }
    #[test]
    fn test_with_static() {
        let altair_source = AltairSource::build()
            .options(AltairConfigOptions {
                window_options: Some(AltairWindowOptions {
                    endpoint_url: Some("/".to_owned()),
                    subscriptions_endpoint: Some("/ws".to_owned()),
                    ..Default::default()
                }),
                ..Default::default()
            })
            .finish();
        assert_eq!(
            altair_source,
            r#"<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Altair</title>
    <base href="https://unpkg.com/altair-static@latest/build/dist/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        AltairGraphQL.init({"endpointURL":"/","subscriptionsEndpoint":"/ws"});
      });
    </script>
    <app-root>
      <style>
        .loading-screen {
          /*Prevents the loading screen from showing until CSS is downloaded*/
          display: none;
        }
      </style>
      <div class="loading-screen styled">
        <div class="loading-screen-inner">
          <div class="loading-screen-logo-container">
            <img src="assets/img/logo_350.svg" alt="Altair">
          </div>
          <div class="loading-screen-loading-indicator">
            <span class="loading-indicator-dot"></span>
            <span class="loading-indicator-dot"></span>
            <span class="loading-indicator-dot"></span>
          </div>
        </div>
      </div>
    </app-root>
    <script type="text/javascript" src="runtime.js"></script>
    <script type="text/javascript" src="polyfills.js"></script>
    <script type="text/javascript" src="main.js"></script>
  </body>
</html>"#
        )
    }
}