use super::*;

pub(super) fn skinned_mesh_renderer_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let mesh_candidates = static_mesh_asset_ref_candidates(world, AssetRefCandidateKind::Mesh);
    let material_candidates = brush_face_material_ref_candidates(world);
    let material_drop_candidate = world
        .get_resource::<EditorAssets>()
        .and_then(|assets| assets.dragging_selection().cloned())
        .and_then(|selection| {
            asset_ref_candidate_from_selection(world, &selection, AssetRefCandidateKind::Material)
        });
    let invalid_material_drop =
        material_drop_invalid_reason(world, material_drop_candidate.is_some());
    let Some(mut renderer) = world.get::<SkinnedMeshRenderer>(entity).cloned() else {
        return;
    };
    let original = renderer.clone();
    let mut changed = false;
    let mut accepted_drop = false;
    let mut browse_mesh = false;
    let mut locate_mesh = false;
    let mut options = ComponentCardOptions::removable(
        COMPONENT_SKINNED_MESH_RENDERER,
        "Skinned Mesh Renderer",
        icons::PERSON_SIMPLE_RUN,
    );
    options.active_toggle = false;
    options.removable = world.get::<AnimationControllerDesc>(entity).is_none();
    options.resettable = false;
    options.copyable = false;
    options.summary = "Skeleton-bound renderer";
    options.body_margin = 0;
    let context = component_card_context(world, entity, options);
    let response = component_card(ui, &context, |ui| {
        let renderer_open_id = ui.make_persistent_id(("skinned_renderer_open", entity));
        let mut renderer_open = ui
            .ctx()
            .data_mut(|data| data.get_persisted::<bool>(renderer_open_id))
            .unwrap_or(true);
        let source_candidate = mesh_candidates
            .iter()
            .find(|candidate| candidate.reference.asset_id == renderer.asset_id);
        let label = Path::new(&renderer.path)
            .file_stem()
            .and_then(|stem| stem.to_str())
            .filter(|label| !label.trim().is_empty())
            .unwrap_or("Skinned mesh");
        let thumbnail = source_candidate.and_then(|candidate| candidate.texture_id);

        renderer_panel(ui, |ui| {
            renderer_panel_header(
                ui,
                RendererPanelHeaderViewModel {
                    index: 0,
                    label,
                    path: if renderer.path.trim().is_empty() {
                        "No model source assigned"
                    } else {
                        renderer.path.as_str()
                    },
                    slot_count: renderer.materials.slots.len(),
                    texture_id: thumbnail,
                },
                &mut renderer_open,
                |ui| {
                    if editor_ui::design_system::controls::icon_button(
                        ui,
                        icons::CROSSHAIR,
                        "Locate model",
                        source_candidate.is_some(),
                    )
                    .clicked()
                    {
                        locate_mesh = true;
                    }
                    if editor_ui::design_system::controls::icon_button(
                        ui,
                        icons::FOLDER_OPEN,
                        "Browse models",
                        true,
                    )
                    .clicked()
                    {
                        browse_mesh = true;
                    }
                },
            );
            if !renderer_open {
                return;
            }
            egui::Frame::new()
                .inner_margin(egui::Margin::symmetric(12, 10))
                .show(ui, |ui| {
                    renderer_properties_header(ui);
                    property_row(ui, "Source", |ui| {
                        ui.add(
                            egui::Label::new(if renderer.path.trim().is_empty() {
                                "Unassigned"
                            } else {
                                renderer.path.as_str()
                            })
                            .truncate(),
                        );
                    });
                    property_row(ui, "Scene", |ui| {
                        ui.label(renderer.scene_index.to_string());
                    });
                    property_row(ui, "Asset ID", |ui| {
                        ui.add(
                            egui::Label::new(if renderer.asset_id.trim().is_empty() {
                                "Unresolved"
                            } else {
                                renderer.asset_id.as_str()
                            })
                            .truncate(),
                        );
                    });
                    ui.small(
                        egui::RichText::new(
                            "Imported skeleton hierarchy and skin bindings remain model-owned.",
                        )
                        .color(TEXT_DIM),
                    );
                });
            renderer_material_slots_header(ui, renderer.materials.slots.len());
            if renderer.materials.slots.is_empty() {
                egui::Frame::new()
                    .inner_margin(egui::Margin::symmetric(12, 10))
                    .show(ui, |ui| {
                        ui.small(
                            egui::RichText::new("No imported slots; reimport the source model")
                                .color(TEXT_DIM),
                        );
                    });
            }
            for (index, slot) in renderer.materials.slots.iter_mut().enumerate() {
                let widget = renderer_material_slot_row(ui, index, |ui| {
                    material_slot_widget_ui(
                        world,
                        ui,
                        entity,
                        slot,
                        MaterialSlotWidgetContext {
                            candidates: &material_candidates,
                            drop_candidate: material_drop_candidate.as_ref(),
                            invalid_drop_reason: invalid_material_drop,
                            expanded_header_when_parameters_closed: true,
                        },
                    )
                });
                changed |= widget.changed;
                accepted_drop |= widget.accepted_drop;
            }
        });
        ui.ctx()
            .data_mut(|data| data.insert_persisted(renderer_open_id, renderer_open));
        for orphan in &renderer.materials.orphaned_assignments {
            ui.label(
                egui::RichText::new(format!(
                    "Orphaned: {} ({})",
                    orphan.last_known_name, orphan.slot_id.0
                ))
                .small()
                .color(egui::Color32::YELLOW),
            );
        }
        if world.get::<AnimationControllerDesc>(entity).is_some() {
            ui.label(
                egui::RichText::new(
                    "Remove the Animation Controller before removing its renderer.",
                )
                .small()
                .color(TEXT_MUTED),
            );
        }
    });
    apply_component_card_response(world, entity, response);
    if browse_mesh {
        crate::ui::request_editor_tab(world, crate::ui::EditorTab::AssetBrowser);
    }
    if locate_mesh {
        if let Some(candidate) = mesh_candidates
            .iter()
            .find(|candidate| candidate.reference.asset_id == renderer.asset_id)
        {
            reveal_asset_in_browser(world, &candidate.folder_path, &candidate.selection);
        }
    }
    if accepted_drop {
        clear_asset_drag(world);
    }
    if changed && renderer != original {
        let result = reflected_component_transaction(
            world,
            entity,
            "Assign Skinned Material Slot",
            shared::AUTHORING_COMPONENT_SKINNED_MESH_RENDERER,
            COMPONENT_SKINNED_MESH_RENDERER,
            move |world, entity| {
                world.entity_mut(entity).insert(renderer);
                Ok(())
            },
        );
        if let Err(error) = result {
            world.resource_mut::<crate::scene_io::SceneIo>().status = error;
        }
    }
}

pub(super) fn static_mesh_renderer_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let mesh_candidates = static_mesh_asset_ref_candidates(world, AssetRefCandidateKind::Mesh);
    let material_candidates = brush_face_material_ref_candidates(world);
    let material_drop_candidate = world
        .get_resource::<EditorAssets>()
        .and_then(|assets| assets.dragging_selection().cloned())
        .and_then(|selection| {
            asset_ref_candidate_from_selection(world, &selection, AssetRefCandidateKind::Material)
        });
    let invalid_material_drop =
        material_drop_invalid_reason(world, material_drop_candidate.is_some());
    let Some(mut renderer) = world.get::<StaticMeshRenderer>(entity).cloned() else {
        return;
    };
    let original = renderer.clone();
    let mut changed = false;
    let mut accepted_drop = false;
    let mut remove_entry = None;
    let mut browse_mesh = false;
    let mut locate_mesh = None;
    for index in 0..renderer.slots.len() {
        ensure_slot_id(&mut renderer.slots[index], index);
        let part = &renderer.slots[index];
        if renderer.materials.slot(&part.material_slot_id).is_none() {
            renderer.materials.slots.push(shared::MaterialSlot {
                id: part.material_slot_id.clone(),
                name: part.name.clone(),
                material: None,
            });
        }
    }

    let mut options = ComponentCardOptions::removable(
        COMPONENT_STATIC_MESH_RENDERER,
        "Static Mesh Renderer",
        icons::CUBE,
    );
    options.summary = "Renderer array";
    options.body_margin = 0;
    let card = component_card_context(world, entity, options);
    let card_response = component_card(ui, &card, |ui| {
        let add_slot = renderer_array_header(ui, renderer.slots.len());
        ui.add_space(26.0);
        if renderer.slots.is_empty() {
            ui.label(egui::RichText::new("No renderer slots").color(TEXT_DIM));
        }

        let (entries, material_set) = (&mut renderer.slots, &mut renderer.materials);
        for (index, entry) in entries.iter_mut().enumerate() {
            ensure_slot_id(entry, index);
            let thumbnail = thumbnail_for_mesh(&entry.mesh, &mesh_candidates);
            let renderer_open_id = ui.make_persistent_id(("static_renderer_open", &entry.id.0));
            let mut renderer_open = ui
                .ctx()
                .data_mut(|data| data.get_persisted::<bool>(renderer_open_id))
                .unwrap_or(index == 0);

            renderer_panel(ui, |ui| {
                renderer_panel_header(
                    ui,
                    RendererPanelHeaderViewModel {
                        index,
                        label: &entry.name,
                        path: entry
                            .mesh
                            .source_path
                            .as_deref()
                            .unwrap_or(&entry.mesh.sub_asset_id),
                        slot_count: 1,
                        texture_id: thumbnail,
                    },
                    &mut renderer_open,
                    |ui| {
                        editor_ui::design_system::controls::icon_button(
                            ui,
                            icons::DOTS_THREE_VERTICAL,
                            "Renderer actions",
                            true,
                        );
                        if editor_ui::design_system::controls::icon_button(
                            ui,
                            icons::TRASH,
                            "Remove slot",
                            true,
                        )
                        .clicked()
                        {
                            remove_entry = Some(index);
                        }
                        if editor_ui::design_system::controls::icon_button(
                            ui,
                            icons::CROSSHAIR,
                            "Locate mesh",
                            true,
                        )
                        .clicked()
                        {
                            locate_mesh = Some(entry.mesh.clone());
                        }
                        if editor_ui::design_system::controls::icon_button(
                            ui,
                            icons::FOLDER_OPEN,
                            "Browse mesh",
                            true,
                        )
                        .clicked()
                        {
                            browse_mesh = true;
                        }
                    },
                );
                if !renderer_open {
                    return;
                }
                egui::Frame::new()
                    .inner_margin(egui::Margin::symmetric(12, 10))
                    .show(ui, |ui| {
                        renderer_properties_header(ui);
                        ui.add_space(2.0);
                        property_row(ui, "Name", |ui| {
                            changed |= ui
                                .add_sized(
                                    [text_field_width(ui), 20.0],
                                    egui::TextEdit::singleline(&mut entry.name),
                                )
                                .changed();
                        });
                        ui.add_space(4.0);
                        let mesh_response = asset_selector_row(
                            ui,
                            "Mesh",
                            icons::CUBE,
                            Some(&entry.mesh),
                            None,
                            false,
                            &mesh_candidates,
                            None,
                            None,
                            AssetSelectorActions::Full,
                        );
                        if let Some(selected) = mesh_response.selected {
                            entry.mesh = selected;
                            changed = true;
                        }
                        if mesh_response.locate {
                            locate_asset_ref(world, Some(&entry.mesh), &mesh_candidates);
                        }
                        ui.add_space(4.0);
                        ui.horizontal_wrapped(|ui| {
                            changed |= ui.checkbox(&mut entry.visible, "Visible").changed();
                            changed |= ui
                                .checkbox(&mut entry.cast_shadows, "Cast shadows")
                                .changed();
                            changed |= ui
                                .checkbox(&mut entry.receive_shadows, "Receive shadows")
                                .changed();
                        });
                        ui.add_space(4.0);
                    });
                renderer_material_slots_header(ui, 1);
                if let Some(slot) = material_set.slot_mut(&entry.material_slot_id) {
                    let widget = renderer_material_slot_row(ui, index, |ui| {
                        material_slot_widget_ui(
                            world,
                            ui,
                            entity,
                            slot,
                            MaterialSlotWidgetContext {
                                candidates: &material_candidates,
                                drop_candidate: material_drop_candidate.as_ref(),
                                invalid_drop_reason: invalid_material_drop,
                                expanded_header_when_parameters_closed: true,
                            },
                        )
                    });
                    changed |= widget.changed;
                    accepted_drop |= widget.accepted_drop;
                }
            });
            ui.ctx()
                .data_mut(|data| data.insert_persisted(renderer_open_id, renderer_open));
        }

        if add_slot {
            let index = renderer.slots.len();
            let entry = StaticMeshRendererEntry {
                id: ComponentInstanceId::new(format!("slot:{index}")),
                material_slot_id: ComponentInstanceId::new(format!("slot:manual:{index}")),
                ..Default::default()
            };
            renderer.materials.slots.push(shared::MaterialSlot {
                id: entry.material_slot_id.clone(),
                name: format!("Material {index}"),
                material: None,
            });
            renderer.slots.push(entry);
            changed = true;
        }
    });
    apply_component_card_response(world, entity, card_response);
    if browse_mesh {
        crate::ui::request_editor_tab(world, crate::ui::EditorTab::AssetBrowser);
    }
    if let Some(mesh) = locate_mesh.as_ref() {
        locate_asset_ref(world, Some(mesh), &mesh_candidates);
    }
    if accepted_drop {
        clear_asset_drag(world);
    }

    if let Some(index) = remove_entry {
        let removed = renderer.slots.remove(index);
        if let Some(slot_index) = renderer
            .materials
            .slots
            .iter()
            .position(|slot| slot.id == removed.material_slot_id)
        {
            let slot = renderer.materials.slots.remove(slot_index);
            if let Some(material) = slot.material {
                renderer
                    .materials
                    .orphaned_assignments
                    .push(shared::OrphanedMaterialAssignment {
                        slot_id: slot.id,
                        last_known_name: slot.name,
                        material,
                    });
            }
        }
        changed = true;
    }
    if changed && renderer != original {
        set_static_mesh_renderer_with_history(world, entity, renderer);
    }
}
