//! Authoring-layer material resolution shared by Inspector display and guarded transactions.

use super::*;

/// Returns only the actor-owned assignment for an exact surface slot.
pub(super) fn actor_material_assignment_for_slot(
    world: &World,
    entity: Entity,
    slot_id: &ComponentInstanceId,
) -> Option<MaterialRef> {
    world
        .get::<SkinnedMeshRenderer>(entity)
        .and_then(|renderer| renderer.materials.slot(slot_id))
        .and_then(|slot| slot.material.clone())
        .or_else(|| {
            world
                .get::<StaticMeshRenderer>(entity)
                .and_then(|renderer| renderer.materials.slot(slot_id))
                .and_then(|slot| slot.material.clone())
        })
        .or_else(|| {
            world
                .get::<Primitive>(entity)
                .filter(|primitive| primitive.surface.id == *slot_id)
                .and_then(|primitive| primitive.surface.material.clone())
        })
}

/// Resolves authored actor/model/project layers for an exact actor slot.
pub(super) fn effective_material_for_actor_slot(
    world: &World,
    entity: Entity,
    slot_id: &ComponentInstanceId,
) -> Option<MaterialRef> {
    actor_material_assignment_for_slot(world, entity, slot_id)
        .or_else(|| model_material_for_actor_slot(world, entity, slot_id).map(|(base, _)| base))
        .or_else(|| {
            world
                .get_resource::<shared::ProjectContentDefaults>()
                .and_then(|defaults| defaults.default_material.clone())
        })
}

pub(super) fn model_material_for_actor_slot(
    world: &World,
    entity: Entity,
    slot_id: &ComponentInstanceId,
) -> Option<(MaterialRef, crate::ui::materials::MaterialLayerBadge)> {
    let asset_id = world
        .get::<StaticMeshRenderer>(entity)
        .and_then(|renderer| {
            renderer
                .slots
                .iter()
                .find(|part| part.material_slot_id == *slot_id)
                .map(|part| part.mesh.asset_id.clone())
        })
        .or_else(|| {
            world
                .get::<SkinnedMeshRenderer>(entity)
                .map(|renderer| renderer.asset_id.clone())
        })?;
    let record = world
        .get_resource::<AssetRegistry>()?
        .records
        .iter()
        .find(|record| record.id.as_string() == asset_id)?;
    let settings = record.model_import();
    match crate::assets::static_mesh::material_selection(settings, &slot_id.0) {
        ModelMaterialSelection::Project(reference) => Some((
            reference.clone(),
            crate::ui::materials::MaterialLayerBadge::ModelDefault,
        )),
        ModelMaterialSelection::Default => None,
        ModelMaterialSelection::Source => {
            let manifest = settings
                .static_mesh_manifest_path
                .as_deref()
                .and_then(|path| load_static_mesh_manifest(path).ok())?;
            let part = manifest.parts.iter().find(|part| {
                let part_id = if part.id.trim().is_empty() {
                    part_id_from_label(&part.mesh_label)
                } else {
                    part.id.clone()
                };
                slot_id.0 == format!("slot:{part_id}")
            })?;
            let material_id = part
                .material_id
                .clone()
                .filter(|id| !id.trim().is_empty())
                .or_else(|| part.material_label.as_deref().map(material_id_from_label))?;
            Some((
                MaterialRef::new(EditorAssetRef::new(
                    manifest.asset_id,
                    material_id,
                    part.material_slot_name.clone(),
                )),
                crate::ui::materials::MaterialLayerBadge::ModelSource,
            ))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn material_ref(id: &str) -> MaterialRef {
        MaterialRef::new(EditorAssetRef::new(id, "material:source", id))
    }

    #[test]
    fn project_default_is_effective_without_becoming_actor_owned() {
        let mut world = World::new();
        let fallback = material_ref("project-default");
        world.insert_resource(shared::ProjectContentDefaults {
            default_material: Some(fallback.clone()),
        });
        let primitive = Primitive::default();
        let slot_id = primitive.surface.id.clone();
        let entity = world.spawn(primitive).id();

        assert_eq!(
            actor_material_assignment_for_slot(&world, entity, &slot_id),
            None
        );
        assert_eq!(
            effective_material_for_actor_slot(&world, entity, &slot_id),
            Some(fallback)
        );
    }

    #[test]
    fn static_and_skinned_actor_assignments_override_project_defaults() {
        let mut world = World::new();
        world.insert_resource(shared::ProjectContentDefaults {
            default_material: Some(material_ref("project-default")),
        });
        let slot_id = ComponentInstanceId::new("slot:surface");
        let explicit = material_ref("actor-material");
        let materials = shared::MaterialSlotSet {
            slots: vec![shared::MaterialSlot {
                id: slot_id.clone(),
                name: "Surface".into(),
                material: Some(explicit.clone()),
            }],
            orphaned_assignments: Vec::new(),
        };
        let static_entity = world
            .spawn(StaticMeshRenderer {
                materials: materials.clone(),
                ..StaticMeshRenderer::default()
            })
            .id();
        let skinned_entity = world
            .spawn(SkinnedMeshRenderer {
                materials,
                ..SkinnedMeshRenderer::default()
            })
            .id();

        for entity in [static_entity, skinned_entity] {
            assert_eq!(
                actor_material_assignment_for_slot(&world, entity, &slot_id),
                Some(explicit.clone())
            );
            assert_eq!(
                effective_material_for_actor_slot(&world, entity, &slot_id),
                Some(explicit.clone())
            );
        }
    }
}
