use super::EnabledOnly;

mod lsp;

pub use lsp::*;

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum CargoExtensionFeatures {
    Enabled(EnabledOnly),
    Features(CargoExtensionFeatureTree),
}

extension_features! {
    CargoExtensionFeatures,

    #[derive(Debug, Default, Clone, PartialEq)]
    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
    #[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
    #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
    #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
    #[cfg_attr(
        feature = "jsonschema",
        schemars(extend(
            "x-tombi-table-keys-order" = tombi_x_keyword::TableKeysOrder::Ascending
        ))
    )]
    pub struct CargoExtensionFeatureTree {
        /// # Cargo LSP feature options
        pub lsp: Option<CargoLspFeatures>,
    }
}

#[cfg(all(test, feature = "serde"))]
mod tests {
    use crate::ToggleFeatureDefaultFalse;
    use crate::extensions::ToggleFeatureDefaultTrue;

    use super::{CargoDocumentLinkFeatureTree, CargoInlayHintFeatureTree};

    #[test]
    fn cargo_inlay_hint_feature_tree_deserializes_workspace_value_key() {
        let features: CargoInlayHintFeatureTree = serde_json::from_value(serde_json::json!({
            "workspace-value": {
                "enabled": false
            }
        }))
        .expect("workspace-value should deserialize");

        assert_eq!(
            features.workspace_value,
            Some(ToggleFeatureDefaultTrue {
                enabled: Some(false.into()),
            })
        );
    }

    #[test]
    fn cargo_inlay_hint_feature_tree_serializes_workspace_value_key() {
        let value = serde_json::to_value(CargoInlayHintFeatureTree {
            dependency_version: None,
            default_features: None,
            workspace_value: Some(ToggleFeatureDefaultTrue {
                enabled: Some(false.into()),
            }),
        })
        .expect("workspace-value should serialize");

        assert_eq!(
            value.get("workspace-value"),
            Some(&serde_json::json!({
                "enabled": false
            }))
        );
        assert!(value.get("workspace").is_none());
    }

    #[test]
    fn cargo_hover_feature_tree_deserializes_default_features_key() {
        let features: super::CargoHoverFeatureTree = serde_json::from_value(serde_json::json!({
            "default-features": {
                "enabled": false
            }
        }))
        .expect("default-features should deserialize");

        assert_eq!(
            features.default_features,
            Some(ToggleFeatureDefaultTrue {
                enabled: Some(false.into()),
            })
        );
    }

    #[test]
    fn cargo_hover_feature_tree_deserializes_feature_dependencies_key() {
        let features: super::CargoHoverFeatureTree = serde_json::from_value(serde_json::json!({
            "feature-dependencies": {
                "enabled": false
            }
        }))
        .expect("feature-dependencies should deserialize");

        assert_eq!(
            features.feature_dependencies,
            Some(ToggleFeatureDefaultTrue {
                enabled: Some(false.into()),
            })
        );
    }

    #[test]
    fn cargo_hover_feature_tree_serializes_feature_dependencies_key() {
        let value = serde_json::to_value(super::CargoHoverFeatureTree {
            dependency_detail: None,
            default_features: None,
            feature_dependencies: Some(ToggleFeatureDefaultTrue {
                enabled: Some(false.into()),
            }),
        })
        .expect("feature-dependencies should serialize");

        assert_eq!(
            value.get("feature-dependencies"),
            Some(&serde_json::json!({
                "enabled": false
            }))
        );
    }

    #[test]
    fn cargo_hover_feature_tree_serializes_default_features_key() {
        let value = serde_json::to_value(super::CargoHoverFeatureTree {
            dependency_detail: None,
            default_features: Some(ToggleFeatureDefaultTrue {
                enabled: Some(false.into()),
            }),
            feature_dependencies: None,
        })
        .expect("default-features should serialize");

        assert_eq!(
            value.get("default-features"),
            Some(&serde_json::json!({
                "enabled": false
            }))
        );
    }

    #[test]
    fn cargo_document_link_feature_tree_deserializes_workspace_key() {
        let features: CargoDocumentLinkFeatureTree = serde_json::from_value(serde_json::json!({
            "workspace": {
                "enabled": false
            }
        }))
        .expect("workspace should deserialize");

        assert_eq!(
            features.workspace,
            Some(ToggleFeatureDefaultFalse {
                enabled: Some(false.into()),
            })
        );
    }

    #[test]
    fn cargo_document_link_feature_tree_serializes_workspace_key() {
        let value = serde_json::to_value(CargoDocumentLinkFeatureTree {
            cargo_toml: None,
            workspace: Some(ToggleFeatureDefaultFalse {
                enabled: Some(false.into()),
            }),
            git: None,
            path: None,
            crates_io: None,
        })
        .expect("workspace should serialize");

        assert_eq!(
            value.get("workspace"),
            Some(&serde_json::json!({
                "enabled": false
            }))
        );
    }
}
