use super::*;

pub(super) fn clear_asset_drag(world: &mut World) {
    if let Some(mut assets) = world.get_resource_mut::<EditorAssets>() {
        assets.clear_drag();
    }
}

pub(super) fn primitive_editor_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    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 primitive) = world.get::<Primitive>(entity).cloned() else {
        return;
    };
    let original = primitive.clone();
    let mut changed = false;
    let mut accepted_drop = false;
    let mut options =
        ComponentCardOptions::removable(COMPONENT_PRIMITIVE, "Primitive", icons::CUBE);
    options.summary = "Shape · Size · Materials";
    let card = component_card_context(world, entity, options);
    let card_response = component_card(ui, &card, |ui| {
        property_row(ui, "Shape", |ui| {
            ui.horizontal_wrapped(|ui| {
                for (label, shape) in [
                    ("Box", PrimitiveShape::Box),
                    ("Sphere", PrimitiveShape::Sphere),
                    ("Ramp", PrimitiveShape::Ramp),
                ] {
                    changed |= ui
                        .selectable_value(&mut primitive.shape, shape, label)
                        .changed();
                }
            });
        });
        property_row(ui, "Size X", |ui| {
            changed |= ui
                .add(egui::Slider::new(&mut primitive.size.x, 0.1..=1000.0))
                .changed();
        });
        property_row(ui, "Size Y", |ui| {
            changed |= ui
                .add(egui::Slider::new(&mut primitive.size.y, 0.1..=1000.0))
                .changed();
        });
        property_row(ui, "Size Z", |ui| {
            changed |= ui
                .add(egui::Slider::new(&mut primitive.size.z, 0.1..=1000.0))
                .changed();
        });
        ui.add_space(8.0);
        let materials = crate::ui::materials::MaterialsSectionViewModel {
            slot_ids: vec![primitive.surface.id.0.clone()],
        };
        let widget = crate::ui::materials::materials_section(ui, &materials, |ui| {
            material_slot_widget_ui(
                world,
                ui,
                entity,
                &mut primitive.surface,
                MaterialSlotWidgetContext {
                    candidates: &material_candidates,
                    drop_candidate: material_drop_candidate.as_ref(),
                    invalid_drop_reason: invalid_material_drop,
                    expanded_header_when_parameters_closed: false,
                },
            )
        });
        changed |= widget.changed;
        accepted_drop |= widget.accepted_drop;
    });
    apply_component_card_response(world, entity, card_response);
    if accepted_drop {
        clear_asset_drag(world);
    }
    if changed && primitive != original {
        set_primitive_with_history(world, entity, primitive);
    }
}

pub(super) fn light_editor_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let Some(mut light) = world.get::<LightDesc>(entity).cloned() else {
        return;
    };
    let _original = light.clone();
    let mut changed = false;
    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(COMPONENT_LIGHT_DESC, "Light", icons::LIGHTBULB),
    );
    let card_response = component_card(ui, &card, |ui| {
        property_row(ui, "Kind", |ui| {
            ui.horizontal_wrapped(|ui| {
                for (label, kind) in [
                    ("Point", AuthoringLightKind::Point),
                    ("Spot", AuthoringLightKind::Spot),
                    ("Directional", AuthoringLightKind::Directional),
                ] {
                    if ui.selectable_label(light.kind == kind, label).clicked() {
                        let color = light.color;
                        light = LightDesc {
                            color,
                            ..LightDesc::for_kind(kind)
                        };
                        changed = true;
                    }
                }
            });
        });
        let solari_disabled = solari_disables_local_light(world, &light);
        if solari_disabled {
            ui.colored_label(
                egui::Color32::from_rgb(255, 180, 100),
                "Point and spot lights are disabled while Solari is active. Use a directional light or emissive material, or switch GI to Forward.",
            );
        }
        ui.add_enabled_ui(!solari_disabled, |ui| {
            let mut color = [light.color.r, light.color.g, light.color.b, light.color.a];
            property_row(ui, "Color", |ui| {
                if ui.color_edit_button_rgba_unmultiplied(&mut color).changed() {
                    light.color = ColorDesc {
                        r: color[0],
                        g: color[1],
                        b: color[2],
                        a: color[3],
                    };
                    changed = true;
                }
            });
            let intensity_label = match light.kind {
                AuthoringLightKind::Directional => "Intensity (lux)",
                _ => "Intensity (lumens)",
            };
            let intensity_max = match light.kind {
                AuthoringLightKind::Directional => AUTHORING_DIRECTIONAL_LUX_MAX,
                _ => AUTHORING_POINT_SPOT_LUMENS_MAX,
            };
            property_row(ui, intensity_label, |ui| {
                ui.horizontal_wrapped(|ui| {
                    if ui
                        .add(
                            egui::Slider::new(&mut light.intensity, 0.0..=intensity_max)
                                .show_value(false),
                        )
                        .changed()
                    {
                        changed = true;
                    }
                    if ui
                        .add_sized(
                            [fit_width(ui, 72.0, 120.0), 20.0],
                            egui::DragValue::new(&mut light.intensity)
                                .range(0.0..=intensity_max)
                                .speed(intensity_max * 0.001),
                        )
                        .changed()
                    {
                        changed = true;
                    }
                });
            });
            if matches!(
                light.kind,
                AuthoringLightKind::Point | AuthoringLightKind::Spot
            ) {
                property_row(ui, "Range (m)", |ui| {
                    changed |= ui
                        .add(egui::Slider::new(&mut light.range, 0.0..=100.0))
                        .changed();
                });
            }
            if matches!(light.kind, AuthoringLightKind::Spot) {
                property_row(ui, "Inner angle", |ui| {
                    changed |= ui
                        .add(egui::Slider::new(&mut light.inner_angle_deg, 1.0..=80.0))
                        .changed();
                });
                property_row(ui, "Outer angle", |ui| {
                    changed |= ui
                        .add(egui::Slider::new(&mut light.outer_angle_deg, 1.0..=90.0))
                        .changed();
                });
            }
            property_row(ui, "Shadows", |ui| {
                changed |= ui.checkbox(&mut light.shadows, "Cast shadows").changed();
            });
            if matches!(light.kind, AuthoringLightKind::Directional) {
                ui.small("Controls project sun while this directional exists.");
            }
        });
    });
    apply_component_card_response(world, entity, card_response);
    if changed {
        set_light_with_history(world, entity, light);
    }
}

pub(super) fn solari_disables_local_light(world: &World, light: &LightDesc) -> bool {
    matches!(
        light.kind,
        AuthoringLightKind::Point | AuthoringLightKind::Spot
    ) && world
        .get_resource::<settings::ActiveCameraRenderProfile>()
        .is_some_and(|profile| profile.gi_path == settings::GiPath::SolariDeferred)
        && world
            .get_resource::<settings::RenderingCapabilities>()
            .is_some_and(|caps| caps.rt_supported)
}

pub(super) fn rigid_body_editor_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let Some(mut body) = world.get::<RigidBodyDesc>(entity).copied() else {
        return;
    };
    let original = body;
    let mut changed = false;
    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(COMPONENT_RIGID_BODY_DESC, "Rigid Body", icons::SPHERE),
    );
    let card_response = component_card(ui, &card, |ui| {
        property_row(ui, "Body", |ui| {
            ui.horizontal_wrapped(|ui| {
                for (label, kind) in [
                    ("Static", AuthoringRigidBody::Static),
                    ("Kinematic", AuthoringRigidBody::Kinematic),
                    ("Dynamic", AuthoringRigidBody::Dynamic),
                ] {
                    changed |= ui.selectable_value(&mut body.body, kind, label).changed();
                }
            });
        });
    });
    apply_component_card_response(world, entity, card_response);
    if changed && body != original {
        set_rigid_body_with_history(world, entity, body);
    }
}

pub(super) fn collider_editor_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let mesh_candidates = static_mesh_asset_ref_candidates(world, AssetRefCandidateKind::Mesh);
    let diagnostic_entry = diagnose_collider(world, entity);
    let Some(mut collider) = world.get::<ColliderDesc>(entity).cloned() else {
        return;
    };
    let original = collider.clone();
    let mut changed = false;
    let renderer_meshes: Vec<EditorAssetRef> = world
        .get::<StaticMeshRenderer>(entity)
        .map(|renderer| {
            renderer
                .slots
                .iter()
                .map(|slot| slot.mesh.clone())
                .collect()
        })
        .unwrap_or_default();

    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(COMPONENT_COLLIDER_DESC, "Collider", icons::SELECTION),
    );
    let card_response = component_card(ui, &card, |ui| {
        if let Some(entry) = diagnostic_entry.as_ref() {
            let (label, color) = match entry.overlay_status {
                ColliderOverlayStatus::Valid => ("Ready", egui::Color32::from_rgb(112, 210, 144)),
                ColliderOverlayStatus::Trigger => {
                    ("Trigger", egui::Color32::from_rgb(86, 195, 235))
                }
                ColliderOverlayStatus::Disabled => {
                    ("Disabled", egui::Color32::from_rgb(143, 151, 163))
                }
                ColliderOverlayStatus::Warning => {
                    ("Warning", egui::Color32::from_rgb(242, 173, 72))
                }
                ColliderOverlayStatus::Error => ("Invalid", egui::Color32::from_rgb(244, 91, 99)),
            };
            ui.horizontal_wrapped(|ui| {
                ui.colored_label(color, egui::RichText::new(label).strong());
                ui.label(egui::RichText::new(entry.shape_label).color(TEXT_DIM));
                if entry.runtime_ready {
                    ui.label(egui::RichText::new("Hydrated").color(TEXT_MUTED).small());
                }
            });
            for diagnostic in &entry.diagnostics {
                let color = match diagnostic.severity {
                    ColliderDiagnosticSeverity::Info => TEXT_MUTED,
                    ColliderDiagnosticSeverity::Warning => egui::Color32::from_rgb(242, 173, 72),
                    ColliderDiagnosticSeverity::Error => egui::Color32::from_rgb(244, 91, 99),
                };
                ui.label(
                    egui::RichText::new(&diagnostic.message)
                        .color(color)
                        .small(),
                );
                ui.label(
                    egui::RichText::new(&diagnostic.repair)
                        .color(TEXT_MUTED)
                        .small(),
                );
            }
            if entry.highest_severity() == Some(ColliderDiagnosticSeverity::Error)
                && ui.small_button("Reset shape").clicked()
            {
                collider.shape = ColliderShapeDesc::default();
                collider.enabled = true;
                changed = true;
            }
            ui.add_space(2.0);
        }
        property_row(ui, "Mode", |ui| {
            changed |= ui.checkbox(&mut collider.enabled, "Enabled").changed();
            changed |= ui.checkbox(&mut collider.is_trigger, "Trigger").changed();
        });
        property_row(ui, "Shape", |ui| {
            let mut shape_kind = collider_shape_kind(&collider.shape);
            egui::ComboBox::from_id_salt(("collider_shape_kind", entity))
                .selected_text(shape_kind)
                .show_ui(ui, |ui| {
                    for label in ["Box", "Sphere", "Capsule", "Static Mesh"] {
                        if ui.selectable_label(shape_kind == label, label).clicked() {
                            shape_kind = label;
                        }
                    }
                });
            if shape_kind != collider_shape_kind(&collider.shape) {
                collider.shape =
                    convert_collider_shape(&collider.shape, shape_kind, renderer_meshes.clone());
                changed = true;
            }
        });

        match &mut collider.shape {
            ColliderShapeDesc::Cuboid {
                x_length,
                y_length,
                z_length,
            } => {
                changed |= dimension_drag(ui, "X", x_length);
                changed |= dimension_drag(ui, "Y", y_length);
                changed |= dimension_drag(ui, "Z", z_length);
            }
            ColliderShapeDesc::Sphere { radius } => {
                changed |= dimension_drag(ui, "Radius", radius);
            }
            ColliderShapeDesc::Capsule { radius, height } => {
                changed |= dimension_drag(ui, "Radius", radius);
                changed |= dimension_drag(ui, "Height", height);
            }
            ColliderShapeDesc::StaticMesh { meshes, .. } => {
                if meshes.is_empty() {
                    ui.label(egui::RichText::new("No mesh collider sources").color(TEXT_DIM));
                }
                for mesh in meshes.iter_mut() {
                    let mesh_response = asset_selector_row(
                        ui,
                        "Mesh",
                        icons::CUBE,
                        Some(mesh),
                        None,
                        false,
                        &mesh_candidates,
                        None,
                        None,
                        AssetSelectorActions::Full,
                    );
                    if let Some(selected) = mesh_response.selected {
                        *mesh = selected;
                        changed = true;
                    }
                    if mesh_response.locate {
                        locate_asset_ref(world, Some(mesh), &mesh_candidates);
                    }
                }
                if !renderer_meshes.is_empty() && ui.button("Use renderer meshes").clicked() {
                    *meshes = renderer_meshes.clone();
                    changed = true;
                }
            }
        }
    });
    apply_component_card_response(world, entity, card_response);

    if changed && collider != original {
        set_collider_with_history(world, entity, collider);
    }
}

pub(super) fn collider_shape_kind(shape: &ColliderShapeDesc) -> &'static str {
    match shape {
        ColliderShapeDesc::Cuboid { .. } => "Box",
        ColliderShapeDesc::Sphere { .. } => "Sphere",
        ColliderShapeDesc::Capsule { .. } => "Capsule",
        ColliderShapeDesc::StaticMesh { .. } => "Static Mesh",
    }
}

pub(super) fn convert_collider_shape(
    previous: &ColliderShapeDesc,
    shape_kind: &str,
    renderer_meshes: Vec<EditorAssetRef>,
) -> ColliderShapeDesc {
    let dimensions = match previous {
        ColliderShapeDesc::Cuboid {
            x_length,
            y_length,
            z_length,
        } => Vec3::new(*x_length, *y_length, *z_length),
        ColliderShapeDesc::Sphere { radius } => Vec3::splat(*radius * 2.0),
        ColliderShapeDesc::Capsule { radius, height } => {
            Vec3::new(*radius * 2.0, *height, *radius * 2.0)
        }
        ColliderShapeDesc::StaticMesh { .. } => Vec3::ONE,
    }
    .max(Vec3::splat(0.001));

    match shape_kind {
        "Sphere" => ColliderShapeDesc::Sphere {
            radius: dimensions.max_element() * 0.5,
        },
        "Capsule" => ColliderShapeDesc::Capsule {
            radius: dimensions.x.max(dimensions.z) * 0.5,
            height: dimensions.y,
        },
        "Static Mesh" => ColliderShapeDesc::static_mesh(renderer_meshes),
        _ => ColliderShapeDesc::Cuboid {
            x_length: dimensions.x,
            y_length: dimensions.y,
            z_length: dimensions.z,
        },
    }
}

pub(super) fn dimension_drag(ui: &mut egui::Ui, label: &str, value: &mut f32) -> bool {
    property_row(ui, label, |ui| {
        ui.add_sized(
            [fit_width(ui, 72.0, 120.0), 20.0],
            egui::DragValue::new(value)
                .range(0.001..=10_000.0)
                .speed(0.05)
                .min_decimals(2)
                .max_decimals(3),
        )
        .changed()
    })
}

pub(super) fn physics_editor_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let Some(mut body) = world.get::<PhysicsBody>(entity).cloned() else {
        return;
    };
    let _original = body.clone();
    let mut changed = false;
    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(COMPONENT_PHYSICS_BODY, "Physics", icons::SPHERE),
    );
    let card_response = component_card(ui, &card, |ui| {
        property_row(ui, "Body", |ui| {
            ui.horizontal_wrapped(|ui| {
                for (label, kind) in [
                    ("Static", AuthoringRigidBody::Static),
                    ("Kinematic", AuthoringRigidBody::Kinematic),
                    ("Dynamic", AuthoringRigidBody::Dynamic),
                ] {
                    if ui.selectable_label(body.body == kind, label).clicked() {
                        body.body = kind;
                        changed = true;
                    }
                }
            });
        });
    });
    apply_component_card_response(world, entity, card_response);
    if changed {
        set_physics_with_history(world, entity, body);
    }
}

pub(super) fn player_spawn_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    if world.get::<PlayerSpawn>(entity).is_none() {
        return;
    }
    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(
            COMPONENT_PLAYER_SPAWN,
            "Player Spawn",
            icons::PERSON_SIMPLE_RUN,
        ),
    );
    let card_response = component_card(ui, &card, |ui| {
        ui.label(
            egui::RichText::new("Uses this actor transform as a player start.").color(TEXT_DIM),
        );
    });
    apply_component_card_response(world, entity, card_response);
}

pub(super) fn weapon_spawn_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let Some(mut spawn) = world.get::<WeaponSpawn>(entity).cloned() else {
        return;
    };
    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(COMPONENT_WEAPON_SPAWN, "Weapon Spawn", icons::CROSSHAIR),
    );
    let card_response = component_card(ui, &card, |ui| {
        property_row(ui, "Weapon ID", |ui| {
            ui.add_sized(
                [text_field_width(ui), 20.0],
                egui::TextEdit::singleline(&mut spawn.weapon_id),
            );
        });
    });
    apply_component_card_response(world, entity, card_response);
    if let Ok(mut e) = world.get_entity_mut(entity) {
        e.insert(spawn);
    }
}

pub(super) fn trigger_volume_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let Some(mut trigger) = world.get::<TriggerVolume>(entity).cloned() else {
        return;
    };
    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(
            COMPONENT_TRIGGER_VOLUME,
            "Trigger Volume",
            icons::SELECTION,
        ),
    );
    let card_response = component_card(ui, &card, |ui| {
        property_row(ui, "Event", |ui| {
            ui.add_sized(
                [text_field_width(ui), 20.0],
                egui::TextEdit::singleline(&mut trigger.event_name),
            );
        });
        property_row(ui, "Half X", |ui| {
            ui.add(egui::Slider::new(&mut trigger.half_extents.x, 0.1..=50.0));
        });
        property_row(ui, "Half Y", |ui| {
            ui.add(egui::Slider::new(&mut trigger.half_extents.y, 0.1..=50.0));
        });
        property_row(ui, "Half Z", |ui| {
            ui.add(egui::Slider::new(&mut trigger.half_extents.z, 0.1..=50.0));
        });
    });
    apply_component_card_response(world, entity, card_response);
    if let Ok(mut e) = world.get_entity_mut(entity) {
        e.insert(trigger);
    }
}

pub(super) fn team_spawn_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let Some(mut spawn) = world.get::<TeamSpawn>(entity).cloned() else {
        return;
    };
    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(COMPONENT_TEAM_SPAWN, "Team Spawn", icons::FLAG),
    );
    let card_response = component_card(ui, &card, |ui| {
        property_row(ui, "Team", |ui| {
            ui.add(egui::Slider::new(&mut spawn.team_id, 0..=8));
        });
    });
    apply_component_card_response(world, entity, card_response);
    if let Ok(mut e) = world.get_entity_mut(entity) {
        e.insert(spawn);
    }
}

pub(super) fn objective_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let Some(mut marker) = world.get::<ObjectiveMarker>(entity).cloned() else {
        return;
    };
    let card = component_card_context(
        world,
        entity,
        ComponentCardOptions::removable(COMPONENT_OBJECTIVE_MARKER, "Objective", icons::TARGET),
    );
    let card_response = component_card(ui, &card, |ui| {
        property_row(ui, "Objective ID", |ui| {
            ui.add_sized(
                [text_field_width(ui), 20.0],
                egui::TextEdit::singleline(&mut marker.objective_id),
            );
        });
    });
    apply_component_card_response(world, entity, card_response);
    if let Ok(mut e) = world.get_entity_mut(entity) {
        e.insert(marker);
    }
}

pub(super) fn prefab_instance_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    crate::assets::prefab_overrides::prefab_instance_inspector_ui(world, ui, entity);
}
