//! Versioned prefab override data and runtime application.

use std::collections::{HashMap, HashSet};

use bevy::ecs::reflect::ReflectComponent;
use bevy::prelude::*;
use bevy::reflect::serde::TypedReflectDeserializer;
use bevy::reflect::{GetPath, ParsedPath, ReflectPath};
use bevy::world_serialization::{WorldInstanceReady, WorldInstanceSpawner};
use serde::de::{DeserializeOwned, DeserializeSeed};
use serde::{Deserialize, Serialize};

use crate::{ActorId, EditorVisibility, HydratedPrefabMember, MaterialDesc, PrefabInstance};

pub const PREFAB_OVERRIDE_FORMAT_VERSION: u32 = 2;

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct PrefabActorPath {
    #[serde(default)]
    pub instance_chain: Vec<String>,
    pub actor_id: String,
}

impl PrefabActorPath {
    pub fn direct(actor_id: impl Into<String>) -> Self {
        Self {
            instance_chain: Vec::new(),
            actor_id: actor_id.into(),
        }
    }

    fn belongs_to_layer(&self, instance_chain: &[String]) -> bool {
        self.instance_chain == instance_chain
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PrefabPropertyOverride {
    pub target: PrefabActorPath,
    pub component_type: String,
    /// Bevy reflection path within the component, such as `base_color.r`.
    pub property_path: String,
    /// Full component value observed when the override was created.
    pub base_component_ron: String,
    /// Full component value containing the overridden property.
    pub value_component_ron: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PrefabComponentOverride {
    pub target: PrefabActorPath,
    pub component_type: String,
    /// Source component value, or `None` when this component was locally added.
    pub base_component_ron: Option<String>,
    /// Replacement component value, or `None` to remove the source component.
    pub value_component_ron: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum PrefabStructuralOverride {
    RemoveChild {
        target: PrefabActorPath,
        base_subtree_revision: String,
    },
    ReparentChild {
        target: PrefabActorPath,
        base_parent: Option<PrefabActorPath>,
        new_parent: Option<PrefabActorPath>,
        /// Source transform observed when the structural override was authored.
        #[serde(default)]
        base_transform_ron: Option<String>,
        /// Local transform after reparenting, preserving the authored world-space pose.
        #[serde(default)]
        local_transform_ron: Option<String>,
    },
    AttachLocalChild {
        local_actor_id: String,
        parent: PrefabActorPath,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PrefabOverrides {
    #[serde(default = "default_format_version")]
    pub format_version: u32,
    #[serde(default)]
    pub source_revision: Option<String>,
    /// Legacy anchor override retained for wire compatibility. New authoring does not emit it.
    #[serde(default)]
    pub transform: Option<Transform>,
    /// Legacy anchor override retained for wire compatibility. New authoring does not emit it.
    #[serde(default)]
    pub material: Option<MaterialDesc>,
    /// V1 direct-child visibility overrides, keyed by ActorId.
    #[serde(default)]
    pub child_visibility: HashMap<String, bool>,
    #[serde(default)]
    pub properties: Vec<PrefabPropertyOverride>,
    #[serde(default)]
    pub components: Vec<PrefabComponentOverride>,
    #[serde(default)]
    pub structural: Vec<PrefabStructuralOverride>,
}

impl Default for PrefabOverrides {
    fn default() -> Self {
        Self {
            format_version: PREFAB_OVERRIDE_FORMAT_VERSION,
            source_revision: None,
            transform: None,
            material: None,
            child_visibility: HashMap::new(),
            properties: Vec::new(),
            components: Vec::new(),
            structural: Vec::new(),
        }
    }
}

const fn default_format_version() -> u32 {
    1
}

pub fn decode_prefab_overrides(instance: &PrefabInstance) -> Result<PrefabOverrides, String> {
    instance.overrides_ron.as_ref().map_or_else(
        || Ok(PrefabOverrides::default()),
        |text| {
            let overrides: PrefabOverrides = ron::from_str(text)
                .map_err(|error| format!("invalid prefab override data: {error}"))?;
            validate_prefab_overrides_payload(&overrides)?;
            Ok(overrides)
        },
    )
}

pub fn validate_prefab_overrides_payload(overrides: &PrefabOverrides) -> Result<(), String> {
    if !(1..=PREFAB_OVERRIDE_FORMAT_VERSION).contains(&overrides.format_version) {
        return Err(format!(
            "unsupported prefab override format version {}",
            overrides.format_version
        ));
    }
    for property in &overrides.properties {
        ron::from_str::<ron::Value>(&property.base_component_ron).map_err(|error| {
            format!(
                "invalid base value for `{}`: {error}",
                property.component_type
            )
        })?;
        ron::from_str::<ron::Value>(&property.value_component_ron).map_err(|error| {
            format!(
                "invalid override value for `{}`: {error}",
                property.component_type
            )
        })?;
        ParsedPath::parse(&property.property_path).map_err(|error| {
            format!(
                "invalid property path `{}`: {error}",
                property.property_path
            )
        })?;
    }
    for component in &overrides.components {
        for value in [
            component.base_component_ron.as_deref(),
            component.value_component_ron.as_deref(),
        ]
        .into_iter()
        .flatten()
        {
            ron::from_str::<ron::Value>(value).map_err(|error| {
                format!(
                    "invalid component value for `{}`: {error}",
                    component.component_type
                )
            })?;
        }
    }
    for structural in &overrides.structural {
        if let PrefabStructuralOverride::ReparentChild {
            base_transform_ron,
            local_transform_ron,
            ..
        } = structural
        {
            for value in [
                base_transform_ron.as_deref(),
                local_transform_ron.as_deref(),
            ]
            .into_iter()
            .flatten()
            {
                ron::from_str::<Transform>(value)
                    .map_err(|error| format!("invalid reparent transform: {error}"))?;
            }
        }
    }
    Ok(())
}

/// Applies one reflected property path while preserving every other field in the current value.
///
/// Navigation baking consumes authored prefab overrides without instantiating a Bevy world. Keep
/// its serialized merge behavior identical to the runtime override path, including enum variants.
pub fn apply_serialized_navigation_property_override(
    component_type: &str,
    current_component_ron: &str,
    value_component_ron: &str,
    property_path: &str,
) -> Result<String, String> {
    fn apply<T>(current: &str, value: &str, property_path: &str) -> Result<String, String>
    where
        T: Reflect + Serialize + DeserializeOwned,
    {
        let mut current = ron::from_str::<T>(current)
            .map_err(|error| format!("invalid current component value: {error}"))?;
        let value = ron::from_str::<T>(value)
            .map_err(|error| format!("invalid override component value: {error}"))?;
        let path = ParsedPath::parse(property_path)
            .map_err(|error| format!("invalid property path `{property_path}`: {error}"))?;
        let source_value = path
            .reflect_element(&value)
            .map_err(|error| format!("override value path `{property_path}` failed: {error}"))?;
        let target_value = current
            .reflect_path_mut(&path)
            .map_err(|error| format!("target property path `{property_path}` failed: {error}"))?;
        target_value
            .try_apply(source_value)
            .map_err(|error| format!("could not apply `{property_path}`: {error}"))?;
        ron::to_string(&current)
            .map_err(|error| format!("could not encode merged component value: {error}"))
    }

    macro_rules! apply_component {
        ($ty:ty) => {
            if component_type == std::any::type_name::<$ty>() {
                return apply::<$ty>(current_component_ron, value_component_ron, property_path);
            }
        };
    }

    apply_component!(Transform);
    apply_component!(crate::Primitive);
    apply_component!(crate::BrushDesc);
    apply_component!(crate::NavigationBounds);
    apply_component!(crate::NavigationObstacle);
    apply_component!(crate::NavigationArea);
    apply_component!(crate::NavigationLink);
    apply_component!(PrefabInstance);
    apply_component!(crate::PrefabRef);
    Err(format!(
        "component `{component_type}` is not supported by serialized authoring property overrides"
    ))
}

pub fn encode_prefab_overrides(overrides: &PrefabOverrides) -> Result<Option<String>, String> {
    if overrides == &PrefabOverrides::default() {
        Ok(None)
    } else {
        ron::to_string(overrides)
            .map(Some)
            .map_err(|error| format!("could not encode prefab overrides: {error}"))
    }
}

pub fn apply_prefab_overrides_on_ready(
    ready: On<WorldInstanceReady>,
    roots: Query<&PrefabInstance>,
    owners: Query<&HydratedPrefabMember>,
    actor_ids: Query<&ActorId>,
    instance_spawner: Res<WorldInstanceSpawner>,
    mut commands: Commands,
) {
    let members: HashMap<String, Entity> = instance_spawner
        .iter_instance_entities(ready.instance_id)
        .filter_map(|entity| {
            actor_ids
                .get(entity)
                .ok()
                .map(|actor_id| (actor_id.0.clone(), entity))
        })
        .collect();
    if members.is_empty() {
        return;
    }

    let mut current = ready.entity;
    let mut instance_chain = Vec::new();
    let mut seen = HashSet::new();
    while seen.insert(current) {
        if let Ok(instance) = roots.get(current) {
            match decode_prefab_overrides(instance) {
                Ok(overrides) => {
                    let members = members.clone();
                    let chain = instance_chain.clone();
                    let owner_root = current;
                    commands.queue(move |world: &mut World| {
                        if let Err(error) =
                            apply_override_layer(world, owner_root, &chain, &members, &overrides)
                        {
                            warn!("Could not apply prefab overrides: {error}");
                        }
                    });
                }
                Err(error) => warn!("Skipping malformed prefab overrides: {error}"),
            }
        }

        let Ok(owner) = owners.get(current) else {
            break;
        };
        let Ok(actor_id) = actor_ids.get(current) else {
            break;
        };
        instance_chain.insert(0, actor_id.0.clone());
        current = owner.instance_root;
    }
}

fn apply_override_layer(
    world: &mut World,
    owner_root: Entity,
    instance_chain: &[String],
    members: &HashMap<String, Entity>,
    overrides: &PrefabOverrides,
) -> Result<(), String> {
    let mut errors = Vec::new();
    if instance_chain.is_empty() {
        for (actor_id, visible) in &overrides.child_visibility {
            if let Some(entity) = members.get(actor_id).copied() {
                if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
                    entity_mut.insert(EditorVisibility { visible: *visible });
                }
            }
        }
    }

    for structural in &overrides.structural {
        if !matches!(structural, PrefabStructuralOverride::ReparentChild { .. }) {
            continue;
        }
        if let Err(error) =
            apply_structural_override(world, owner_root, instance_chain, members, structural)
        {
            errors.push(error);
        }
    }

    for component in &overrides.components {
        if !component.target.belongs_to_layer(instance_chain) {
            continue;
        }
        let Some(entity) = members.get(&component.target.actor_id).copied() else {
            continue;
        };
        if let Err(error) = apply_component_override(world, entity, component) {
            errors.push(error);
        }
    }
    for property in &overrides.properties {
        if !property.target.belongs_to_layer(instance_chain) {
            continue;
        }
        let Some(entity) = members.get(&property.target.actor_id).copied() else {
            continue;
        };
        if let Err(error) = apply_property_override(world, entity, property) {
            errors.push(error);
        }
    }
    for structural in &overrides.structural {
        if matches!(structural, PrefabStructuralOverride::ReparentChild { .. }) {
            continue;
        }
        if let Err(error) =
            apply_structural_override(world, owner_root, instance_chain, members, structural)
        {
            errors.push(error);
        }
    }
    if errors.is_empty() {
        Ok(())
    } else {
        Err(errors.join("; "))
    }
}

fn apply_component_override(
    world: &mut World,
    entity: Entity,
    component: &PrefabComponentOverride,
) -> Result<(), String> {
    if world.get_entity(entity).is_err() {
        return Err(format!(
            "component override target {:?} no longer exists",
            entity
        ));
    }
    world.resource_scope(|world, registry: Mut<AppTypeRegistry>| {
        let registry = registry.read();
        let registration = registry
            .get_with_type_path(&component.component_type)
            .ok_or_else(|| {
                format!(
                    "component type `{}` is not registered",
                    component.component_type
                )
            })?;
        let reflect_component = registration
            .data::<ReflectComponent>()
            .cloned()
            .ok_or_else(|| {
                format!(
                    "component `{}` is not reflectable",
                    component.component_type
                )
            })?;
        match component.value_component_ron.as_deref() {
            Some(value) => {
                let reflected = deserialize_component(value, registration, &registry)?;
                let exists = reflect_component.reflect(world.entity(entity)).is_some();
                if exists {
                    reflect_component.apply(world.entity_mut(entity), reflected.as_ref());
                } else {
                    reflect_component.insert(
                        &mut world.entity_mut(entity),
                        reflected.as_ref(),
                        &registry,
                    );
                }
            }
            None => reflect_component.remove(&mut world.entity_mut(entity)),
        }
        Ok(())
    })
}

fn apply_property_override(
    world: &mut World,
    entity: Entity,
    property: &PrefabPropertyOverride,
) -> Result<(), String> {
    if world.get_entity(entity).is_err() {
        return Err(format!(
            "property override target {:?} no longer exists",
            entity
        ));
    }
    world.resource_scope(|world, registry: Mut<AppTypeRegistry>| {
        let registry = registry.read();
        let registration = registry
            .get_with_type_path(&property.component_type)
            .ok_or_else(|| {
                format!(
                    "component type `{}` is not registered",
                    property.component_type
                )
            })?;
        let reflect_component = registration
            .data::<ReflectComponent>()
            .cloned()
            .ok_or_else(|| format!("component `{}` is not reflectable", property.component_type))?;
        let reflected =
            deserialize_component(&property.value_component_ron, registration, &registry)?;
        let path = ParsedPath::parse(&property.property_path).map_err(|error| {
            format!(
                "invalid property path `{}`: {error}",
                property.property_path
            )
        })?;
        let source_value = path.reflect_element(reflected.as_ref()).map_err(|error| {
            format!(
                "override value path `{}` failed: {error}",
                property.property_path
            )
        })?;
        let mut target_component = reflect_component
            .reflect_mut(world.entity_mut(entity))
            .ok_or_else(|| format!("target is missing component `{}`", property.component_type))?;
        let target_value = target_component.reflect_path_mut(&path).map_err(|error| {
            format!(
                "target property path `{}` failed: {error}",
                property.property_path
            )
        })?;
        target_value
            .try_apply(source_value)
            .map_err(|error| format!("could not apply `{}`: {error}", property.property_path))
    })
}

fn deserialize_component(
    text: &str,
    registration: &bevy::reflect::TypeRegistration,
    registry: &bevy::reflect::TypeRegistry,
) -> Result<Box<dyn PartialReflect>, String> {
    let mut deserializer =
        ron::de::Deserializer::from_str(text).map_err(|error| error.to_string())?;
    TypedReflectDeserializer::new(registration, registry)
        .deserialize(&mut deserializer)
        .map_err(|error| error.to_string())
}

fn apply_structural_override(
    world: &mut World,
    owner_root: Entity,
    instance_chain: &[String],
    members: &HashMap<String, Entity>,
    structural: &PrefabStructuralOverride,
) -> Result<(), String> {
    match structural {
        PrefabStructuralOverride::RemoveChild { target, .. }
            if target.belongs_to_layer(instance_chain) =>
        {
            if let Some(entity) = members.get(&target.actor_id).copied() {
                if let Ok(entity_mut) = world.get_entity_mut(entity) {
                    entity_mut.despawn();
                }
            }
        }
        PrefabStructuralOverride::ReparentChild {
            target,
            new_parent,
            local_transform_ron,
            ..
        } if target.belongs_to_layer(instance_chain) => {
            let Some(entity) = members.get(&target.actor_id).copied() else {
                return Ok(());
            };
            let parent = match new_parent {
                Some(parent) if !parent.belongs_to_layer(instance_chain) => {
                    return Err(format!(
                        "reparent target `{}` crosses a prefab layer",
                        parent.actor_id
                    ));
                }
                Some(parent) => members
                    .get(&parent.actor_id)
                    .copied()
                    .ok_or_else(|| format!("reparent target `{}` is missing", parent.actor_id))?,
                None => owner_root,
            };
            if world.get_entity(parent).is_err() {
                return Err(format!(
                    "reparent target for `{}` no longer exists",
                    target.actor_id
                ));
            }
            if hierarchy_would_cycle(world, entity, parent) {
                return Err(format!(
                    "reparent override for `{}` would create a hierarchy cycle",
                    target.actor_id
                ));
            }
            if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
                entity_mut.insert(ChildOf(parent));
                if let Some(transform) = local_transform_ron
                    .as_deref()
                    .and_then(|ron| ron::from_str::<Transform>(ron).ok())
                {
                    entity_mut.insert(transform);
                }
            }
        }
        PrefabStructuralOverride::AttachLocalChild {
            local_actor_id,
            parent,
        } if parent.belongs_to_layer(instance_chain) => {
            let Some(parent_entity) = members.get(&parent.actor_id).copied() else {
                return Ok(());
            };
            let local = world
                .query::<(Entity, &ActorId, Option<&HydratedPrefabMember>)>()
                .iter(world)
                .find_map(|(entity, actor_id, hydrated)| {
                    (hydrated.is_none() && actor_id.0 == *local_actor_id && entity != owner_root)
                        .then_some(entity)
                });
            if let Some(local) = local {
                world.entity_mut(local).insert(ChildOf(parent_entity));
            }
        }
        _ => {}
    }
    Ok(())
}

fn hierarchy_would_cycle(world: &World, entity: Entity, parent: Entity) -> bool {
    let mut current = Some(parent);
    let mut seen = HashSet::new();
    while let Some(candidate) = current {
        if candidate == entity || !seen.insert(candidate) {
            return true;
        }
        current = world.get::<ChildOf>(candidate).map(ChildOf::parent);
    }
    false
}

#[cfg(test)]
mod tests {
    use super::*;
    use bevy::asset::AssetPlugin;
    use bevy::world_serialization::{DynamicWorld, DynamicWorldRoot, WorldSerializationPlugin};

    #[test]
    fn legacy_override_payload_decodes_with_v1_version() {
        let instance = PrefabInstance {
            overrides_ron: Some("(child_visibility:{\"child\":false})".to_string()),
            ..Default::default()
        };
        let overrides = decode_prefab_overrides(&instance).unwrap();
        assert_eq!(overrides.format_version, 1);
        assert_eq!(overrides.child_visibility.get("child"), Some(&false));
    }

    #[test]
    fn nested_actor_paths_are_distinct() {
        let first = PrefabActorPath {
            instance_chain: vec!["left".to_string()],
            actor_id: "mesh".to_string(),
        };
        let second = PrefabActorPath {
            instance_chain: vec!["right".to_string()],
            actor_id: "mesh".to_string(),
        };
        assert_ne!(first, second);
    }

    #[test]
    fn serialized_property_merge_preserves_unrelated_fields_and_enum_identity() {
        let current = crate::Primitive::cuboid(Vec3::new(4.0, 0.2, 6.0));
        let value = crate::Primitive {
            shape: crate::PrimitiveShape::Sphere,
            size: Vec3::splat(99.0),
        };
        let merged = apply_serialized_navigation_property_override(
            std::any::type_name::<crate::Primitive>(),
            &ron::to_string(&current).unwrap(),
            &ron::to_string(&value).unwrap(),
            "shape",
        )
        .unwrap();
        let merged: crate::Primitive = ron::from_str(&merged).unwrap();

        assert_eq!(merged.shape, crate::PrimitiveShape::Sphere);
        assert_eq!(merged.size, current.size);
    }

    #[test]
    fn invalid_inner_override_payload_is_rejected_during_decode() {
        let mut instance = PrefabInstance::new("prefab", "assets/prefabs/test.scn.ron");
        instance.overrides_ron = Some(
            ron::to_string(&PrefabOverrides {
                properties: vec![PrefabPropertyOverride {
                    target: PrefabActorPath::direct("mesh"),
                    component_type: std::any::type_name::<MaterialDesc>().to_string(),
                    property_path: "roughness".to_string(),
                    base_component_ron: "not ron".to_string(),
                    value_component_ron: ron::to_string(&MaterialDesc::default()).unwrap(),
                }],
                ..default()
            })
            .unwrap(),
        );

        let error = decode_prefab_overrides(&instance).unwrap_err();
        assert!(error.contains("invalid base value"), "{error}");
    }

    #[test]
    fn shared_runtime_applies_property_override_without_editor_plugin() {
        let mut app = App::new();
        app.add_plugins(MinimalPlugins)
            .add_plugins(AssetPlugin::default())
            .add_plugins(bevy::pbr::MaterialPlugin::<StandardMaterial>::default())
            .init_asset::<Mesh>()
            .add_plugins(WorldSerializationPlugin)
            .insert_resource(settings::ProjectSettings::default())
            .add_plugins(crate::SharedTypesPlugin);

        let base = MaterialDesc::default();
        let mut value = base.clone();
        value.roughness = 0.18;
        let overrides = PrefabOverrides {
            properties: vec![PrefabPropertyOverride {
                target: PrefabActorPath::direct("mesh"),
                component_type: std::any::type_name::<MaterialDesc>().to_string(),
                property_path: "roughness".to_string(),
                base_component_ron: ron::to_string(&base).unwrap(),
                value_component_ron: ron::to_string(&value).unwrap(),
            }],
            ..Default::default()
        };

        let mut source = World::new();
        source.spawn((ActorId::new("mesh"), base));
        let dynamic_world = DynamicWorld::from_world_with(
            &source,
            &app.world().resource::<AppTypeRegistry>().read(),
        );
        let handle = app
            .world_mut()
            .resource_mut::<Assets<DynamicWorld>>()
            .add(dynamic_world);
        let mut instance = PrefabInstance::new("prefab", "assets/prefabs/test.scn.ron");
        instance.overrides_ron = encode_prefab_overrides(&overrides).unwrap();
        app.world_mut().spawn((instance, DynamicWorldRoot(handle)));

        app.update();

        let world = app.world_mut();
        let (_, material) = world
            .query::<(&ActorId, &MaterialDesc)>()
            .single(world)
            .expect("shared runtime should spawn one overridden source actor");
        assert_eq!(material.roughness, 0.18);
    }

    #[test]
    fn runtime_applies_valid_operations_after_an_independent_operation_error() {
        let mut app = App::new();
        app.add_plugins(MinimalPlugins)
            .add_plugins(AssetPlugin::default())
            .add_plugins(bevy::pbr::MaterialPlugin::<StandardMaterial>::default())
            .init_asset::<Mesh>()
            .add_plugins(WorldSerializationPlugin)
            .insert_resource(settings::ProjectSettings::default())
            .add_plugins(crate::SharedTypesPlugin);

        let transform = Transform::default();
        let mut moved = transform;
        moved.translation.x = 5.0;
        let material = MaterialDesc::default();
        let mut edited_material = material.clone();
        edited_material.roughness = 0.09;
        let overrides = PrefabOverrides {
            properties: vec![
                PrefabPropertyOverride {
                    target: PrefabActorPath::direct("mesh"),
                    component_type: std::any::type_name::<Transform>().to_string(),
                    property_path: "translation".to_string(),
                    base_component_ron: ron::to_string(&transform).unwrap(),
                    value_component_ron: ron::to_string(&moved).unwrap(),
                },
                PrefabPropertyOverride {
                    target: PrefabActorPath::direct("mesh"),
                    component_type: std::any::type_name::<MaterialDesc>().to_string(),
                    property_path: "roughness".to_string(),
                    base_component_ron: ron::to_string(&material).unwrap(),
                    value_component_ron: ron::to_string(&edited_material).unwrap(),
                },
            ],
            ..default()
        };

        let mut source = World::new();
        source.spawn((ActorId::new("mesh"), material));
        let dynamic_world = DynamicWorld::from_world_with(
            &source,
            &app.world().resource::<AppTypeRegistry>().read(),
        );
        let handle = app
            .world_mut()
            .resource_mut::<Assets<DynamicWorld>>()
            .add(dynamic_world);
        let mut instance = PrefabInstance::new("prefab", "assets/prefabs/test.scn.ron");
        instance.overrides_ron = encode_prefab_overrides(&overrides).unwrap();
        app.world_mut().spawn((instance, DynamicWorldRoot(handle)));

        app.update();

        let world = app.world_mut();
        let (_, material) = world
            .query::<(&ActorId, &MaterialDesc)>()
            .single(world)
            .unwrap();
        assert_eq!(material.roughness, 0.09);
    }
}
