//! Window → Rendering: active camera, project defaults, and volume list.

use bevy::prelude::*;
use bevy_egui::egui;
use game::rendering::{
    camera_auto_exposure_active, effective_viewport_render_stack, hdr_enabled_profile,
    SolariRaytracingSceneStats,
};
use settings::{
    ActiveCameraRenderProfile, ActiveVolumeContribution, ExposureMode, GiPath, ProjectSettings,
    RenderFallbackReason, RenderingCapabilities,
};
use shared::{
    ActorKind, AuthoringLightKind, LevelObject, LightDesc, PostProcessVolumeDesc, ProjectSun,
};

use crate::history::{spawn_with_history, EditorEntitySnapshot};
use crate::project::settings_ui::{open_project_settings_panel, ProjectSettingsPanel};
use crate::selection::SelectedEntity;
use crate::ui::helpers::focus_editor_camera_on_selection;
use crate::viewport::EditorViewportMode;

#[derive(Resource, Default)]
pub struct RenderingDiagnosticsPanel {
    pub open: bool,
    tab: RenderingPanelTab,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
enum RenderingPanelTab {
    #[default]
    Active,
    Project,
    Volumes,
}

pub fn rendering_diagnostics_window(world: &mut World, ctx: &egui::Context, open: &mut bool) {
    if !*open {
        return;
    }
    egui::Window::new("Rendering")
        .open(open)
        .default_width(440.0)
        .show(ctx, |ui| {
            rendering_diagnostics_ui(world, ui);
        });
}

pub fn rendering_diagnostics_ui(world: &mut World, ui: &mut egui::Ui) {
    let mut tab = world.resource::<RenderingDiagnosticsPanel>().tab;

    ui.horizontal(|ui| {
        ui.selectable_value(&mut tab, RenderingPanelTab::Active, "Active Camera");
        ui.selectable_value(&mut tab, RenderingPanelTab::Project, "Project");
        ui.selectable_value(&mut tab, RenderingPanelTab::Volumes, "Volumes");
    });
    world.resource_mut::<RenderingDiagnosticsPanel>().tab = tab;
    ui.separator();

    match tab {
        RenderingPanelTab::Active => active_camera_tab(world, ui),
        RenderingPanelTab::Project => project_tab(world, ui),
        RenderingPanelTab::Volumes => volumes_tab(world, ui),
    }
}

fn active_camera_tab(world: &mut World, ui: &mut egui::Ui) {
    let profile = world.resource::<ActiveCameraRenderProfile>().clone();
    let caps = world.resource::<RenderingCapabilities>().clone();
    let contributions = world.resource::<ActiveVolumeContribution>().clone();
    let solari_stats = world.get_resource::<SolariRaytracingSceneStats>().copied();
    let local_shadow_lights = game::rendering::world_has_local_shadow_lights(world);
    let stack = effective_viewport_render_stack(
        profile.gi_path,
        &caps,
        solari_stats.as_ref(),
        local_shadow_lights,
    );

    let gi_label = match (
        stack.requested_gi_path,
        stack.effective_gi_path,
        stack.fallback_reason,
    ) {
        (GiPath::Forward, _, _) => "Forward PBR",
        (GiPath::SolariDeferred, GiPath::SolariDeferred, _) => "Solari deferred",
        (GiPath::SolariDeferred, _, Some(RenderFallbackReason::RtUnsupported)) => {
            "Solari requested, Forward (RT unsupported)"
        }
        (GiPath::SolariDeferred, _, None) => "Solari requested",
    };
    ui.label(format!("GI path: {gi_label}"));
    ui.label(format!(
        "Stack: requested={:?}, effective={:?}, fallback={:?}",
        stack.requested_gi_path, stack.effective_gi_path, stack.fallback_reason
    ));
    if stack.requested_gi_path == GiPath::SolariDeferred
        && stack.effective_gi_path == GiPath::SolariDeferred
    {
        ui.colored_label(
            egui::Color32::from_rgb(255, 180, 100),
            "Solari deferred: directional lights and emissive meshes affect lighting; point/spot LightDesc components are disabled in Bevy 0.19.",
        );
    } else if stack.fallback_reason == Some(RenderFallbackReason::RtUnsupported)
        || (!caps.rt_supported && profile.gi_path == GiPath::Forward)
    {
        ui.colored_label(
            egui::Color32::from_rgb(255, 180, 100),
            "Forward fallback — Solari RT features unavailable on this GPU.",
        );
    }
    if stack.requested_gi_path == GiPath::SolariDeferred && !stack.solari_ready {
        ui.colored_label(
            egui::Color32::from_rgb(255, 180, 100),
            "Solari is active/requested but still warming up or missing scene readiness; see Solari raytracing scene below.",
        );
    }

    runtime_lighting_summary(world, ui);
    solari_geometry_summary(world, ui);

    ui.separator();
    let auto_active = camera_auto_exposure_active(&profile, &caps);
    match profile.exposure_mode {
        ExposureMode::Auto if auto_active => {
            ui.label(format!(
                "Exposure: Auto (brighten {:.1} / darken {:.1} f-stops/s)",
                profile.auto_exposure_speed_brighten, profile.auto_exposure_speed_darken
            ));
        }
        ExposureMode::Auto => {
            ui.label(format!(
                "Exposure: Auto requested — using manual {:.2} EV100",
                profile.exposure_ev100
            ));
            if !profile.hdr {
                ui.colored_label(
                    egui::Color32::from_rgb(255, 180, 100),
                    "Fallback: HDR is disabled in project settings.",
                );
            } else if !hdr_enabled_profile(&profile) {
                ui.colored_label(
                    egui::Color32::from_rgb(255, 180, 100),
                    "Fallback: HDR pass off (check BEVY_FPS_HDR or project HDR).",
                );
            } else if !caps.auto_exposure_supported {
                ui.colored_label(
                    egui::Color32::from_rgb(255, 180, 100),
                    "Fallback: GPU compute shaders unavailable.",
                );
            }
        }
        ExposureMode::Manual => {
            ui.label(format!(
                "Exposure: Manual ({:.2} EV100)",
                profile.exposure_ev100
            ));
            let project = world.resource::<ProjectSettings>();
            if project.rendering.exposure_mode == ExposureMode::Auto {
                ui.small("Post-process volume overrides exposure EV100 (manual for this view).");
            }
        }
    }
    ui.label(format!(
        "Fog: rgb({:.2},{:.2},{:.2}) density {:.4}",
        profile.fog_color[0], profile.fog_color[1], profile.fog_color[2], profile.fog_density
    ));
    ui.label(format!(
        "Post-FX: hdr={} target_hdr={} bloom={} taa={} ssao={} atmosphere={} tonemap={}",
        profile.hdr,
        hdr_enabled_profile(&profile)
            || profile.atmosphere
            || stack.effective_gi_path == GiPath::SolariDeferred,
        profile.bloom,
        profile.taa,
        profile.ssao,
        profile.atmosphere,
        profile.tonemapping_aces
    ));
    if let Some(effect) = &profile.fullscreen_effect {
        ui.label(format!("Fullscreen effect: {effect}"));
    }

    ui.separator();
    ui.heading("Contributing volumes");
    if contributions.entries.is_empty() {
        ui.label("No volumes at camera position — using project defaults.");
    } else {
        for entry in &contributions.entries {
            ui.label(format!(
                "{} — {:.0}% weight, {} overrides",
                entry.label,
                entry.weight * 100.0,
                entry.override_count
            ));
        }
    }

    if ui.button("Select overlapping volumes at camera").clicked() {
        select_volumes_at_camera(world);
    }

    if let Some(mode) = world.get_resource::<EditorViewportMode>() {
        ui.separator();
        let mode_label = match *mode {
            EditorViewportMode::Lit => "Lit",
            EditorViewportMode::Unlit => "Unlit",
            EditorViewportMode::Collider => "Collider",
        };
        ui.label(format!("Viewport mode: {mode_label}"));
    }
}

fn runtime_lighting_summary(world: &mut World, ui: &mut egui::Ui) {
    ui.separator();
    ui.heading("Runtime lights");

    let directional = world.query::<&DirectionalLight>().iter(world).count();
    let point = world.query::<&PointLight>().iter(world).count();
    let spot = world.query::<&SpotLight>().iter(world).count();
    let shadowed_directional = world
        .query::<&DirectionalLight>()
        .iter(world)
        .filter(|light| light.shadow_maps_enabled)
        .count();
    let shadowed_point = world
        .query::<&PointLight>()
        .iter(world)
        .filter(|light| light.shadow_maps_enabled)
        .count();
    let shadowed_spot = world
        .query::<&SpotLight>()
        .iter(world)
        .filter(|light| light.shadow_maps_enabled)
        .count();
    ui.label(format!(
        "Counts: {directional} directional, {point} point, {spot} spot"
    ));
    ui.label(format!(
        "Shadow casters: {shadowed_directional} directional, {shadowed_point} point, {shadowed_spot} spot"
    ));

    let scene_directionals: Vec<(f32, bool)> = world
        .query_filtered::<&LightDesc, With<LevelObject>>()
        .iter(world)
        .filter_map(|light| {
            matches!(light.kind, AuthoringLightKind::Directional)
                .then_some((light.intensity, light.shadows))
        })
        .collect();

    if scene_directionals.is_empty() {
        ui.label("Scene sun: none (project sun drives outdoor lighting)");
    } else {
        let (lux, shadows) = scene_directionals[0];
        ui.label(format!(
            "Scene sun override: {lux:.0} lux, shadows={shadows} ({} directional LightDesc in level)",
            scene_directionals.len()
        ));
    }

    let project_sun_active = world
        .query_filtered::<&Visibility, With<ProjectSun>>()
        .iter(world)
        .any(|visibility| *visibility != Visibility::Hidden);

    if project_sun_active {
        ui.label("Project sun: active");
    } else if !scene_directionals.is_empty() {
        ui.label("Project sun: hidden (scene directional override)");
    } else {
        ui.colored_label(
            egui::Color32::from_rgb(255, 140, 140),
            "Project sun: hidden and no scene directional — level may appear black.",
        );
    }

    if directional + point + spot == 0 {
        ui.colored_label(
            egui::Color32::from_rgb(255, 140, 140),
            "Zero runtime lights in the world.",
        );
    }
}

fn solari_geometry_summary(world: &World, ui: &mut egui::Ui) {
    let Some(stats) = world.get_resource::<SolariRaytracingSceneStats>() else {
        return;
    };
    ui.separator();
    ui.heading("Solari raytracing scene");
    ui.label(format!(
        "Project meshes: {} tagged, {} eligible, {} Solari-compatible assets, {} unsupported, {} deformed safely omitted, {} excluded helpers, {} outside level roots",
        stats.tagged_meshes,
        stats.project_meshes,
        stats.compatible_mesh_assets,
        stats.unsupported_meshes,
        stats.deformed_meshes_excluded,
        stats.excluded_meshes,
        stats.missing_level_root_meshes
    ));
    if stats.surface_material_meshes > 0 {
        ui.label(format!(
            "Surface ABI materials: {} meshes, {} missing an active evaluator generation",
            stats.surface_material_meshes, stats.surface_materials_missing_evaluator
        ));
    }
    if stats.deformed_meshes_excluded > 0 {
        ui.colored_label(
            egui::Color32::from_rgb(255, 180, 100),
            "Skinned/morphed meshes are omitted from Solari until dynamic deformed BLAS is available; no bind-pose proxy is traced.",
        );
    }
    if stats.surface_materials_missing_evaluator > 0 {
        ui.colored_label(
            egui::Color32::from_rgb(255, 140, 140),
            "Surface meshes without a validated evaluator are omitted instead of using an approximate ray-tracing material.",
        );
    }
    if stats.incompatible_mesh_assets > 0 {
        ui.colored_label(
            egui::Color32::from_rgb(255, 180, 100),
            format!(
                "Incompatible mesh assets: {} ({} missing tangents, {} missing U32 indices)",
                stats.incompatible_mesh_assets,
                stats.mesh_assets_missing_tangents,
                stats.mesh_assets_missing_u32_indices
            ),
        );
    }
    ui.label(format!(
        "Render scene: {} instances, {} directional lights, bind group ready={}",
        stats.render_instances, stats.render_directional_lights, stats.render_bind_group_ready
    ));
    ui.label(format!(
        "Solari-compatible lights: {} directional/emissive",
        stats.compatible_light_count()
    ));
    if stats.solari_ready() {
        ui.label("Readiness: Solari active");
    } else {
        ui.colored_label(
            egui::Color32::from_rgb(255, 180, 100),
            format!("Readiness: {}", solari_readiness_reason(stats)),
        );
    }
}

fn solari_readiness_reason(stats: &SolariRaytracingSceneStats) -> &'static str {
    if stats.tagged_meshes == 0 {
        "no project meshes are tagged for raytracing"
    } else if stats.compatible_mesh_assets == 0 {
        "tagged meshes are not Solari-compatible mesh assets"
    } else if stats.render_instances == 0 {
        "raytracing meshes have not reached the render world"
    } else if !stats.render_bind_group_ready {
        "Bevy has not built the raytracing scene bind group"
    } else if stats.compatible_light_count() == 0 {
        "no extracted directional or emissive Solari light source"
    } else {
        "waiting for Solari readiness"
    }
}

fn project_tab(world: &mut World, ui: &mut egui::Ui) {
    let rendering = world.resource::<ProjectSettings>().rendering.clone();
    ui.label(format!("GiMode: {:?}", rendering.gi_mode));
    ui.label(format!("HDR: {}", rendering.hdr));
    if rendering.exposure_mode == ExposureMode::Auto {
        ui.label(format!(
            "Exposure: Auto (brighten {:.1} / darken {:.1} f-stops/s)",
            rendering.auto_exposure_speed_brighten, rendering.auto_exposure_speed_darken
        ));
    } else {
        ui.label(format!(
            "Exposure: Manual ({:.2} EV100)",
            rendering.exposure_ev100
        ));
    }
    ui.label(format!(
        "Ambient brightness: {:.1}",
        rendering.ambient_brightness
    ));
    ui.label(format!(
        "Sun illuminance: {:.0} lux",
        rendering.sun_illuminance
    ));
    ui.label(format!(
        "Post-FX: bloom={} taa={} ssao={} atmosphere={}",
        rendering.bloom, rendering.taa, rendering.ssao, rendering.atmosphere
    ));
    if ui.button("Open Project Settings → Rendering").clicked() {
        let mut panel = world.resource_mut::<ProjectSettingsPanel>();
        open_project_settings_panel(&mut panel);
    }
    if ui.button("Scene → Lighting → Use project sun").clicked() {
        crate::ui::helpers::use_project_sun(world);
    }
    if ui.button("Reset scene lighting to project").clicked() {
        crate::ui::helpers::reset_scene_lighting_to_project_defaults(world);
    }
}

fn volumes_tab(world: &mut World, ui: &mut egui::Ui) {
    let mut volumes: Vec<(Entity, PostProcessVolumeDesc, String)> = world
        .query_filtered::<(Entity, &PostProcessVolumeDesc, Option<&Name>), With<LevelObject>>()
        .iter(world)
        .map(|(entity, desc, name)| {
            let label = desc
                .label
                .clone()
                .or_else(|| name.map(|n| n.to_string()))
                .unwrap_or_else(|| format!("Volume {entity:?}"));
            (entity, desc.clone(), label)
        })
        .collect();
    volumes.sort_by(|a, b| b.1.priority.cmp(&a.1.priority).then_with(|| a.0.cmp(&b.0)));

    if volumes.is_empty() {
        ui.label("No post-process volumes in this level.");
        return;
    }

    for (entity, desc, label) in volumes {
        ui.horizontal(|ui| {
            if ui.button(&label).clicked() {
                world.resource_mut::<SelectedEntity>().0 = Some(entity);
            }
            ui.label(format!("priority {}", desc.priority));
            if ui.small_button("Focus").clicked() {
                world.resource_mut::<SelectedEntity>().0 = Some(entity);
                focus_editor_camera_on_selection(world);
            }
        });
    }
}

pub fn select_volumes_at_camera(world: &mut World) {
    use game_hot::sample_volumes_at;
    use settings::ProjectRenderCamera;

    let camera_pos = world
        .query_filtered::<&GlobalTransform, With<ProjectRenderCamera>>()
        .iter(world)
        .next()
        .map(|t| t.translation())
        .unwrap_or(Vec3::ZERO);

    let volume_list: Vec<_> = world
        .query_filtered::<(Entity, &PostProcessVolumeDesc, &GlobalTransform), With<LevelObject>>()
        .iter(world)
        .collect();
    let samples = sample_volumes_at(camera_pos, &volume_list);

    if !samples.is_empty() {
        world.resource_mut::<SelectedEntity>().0 = Some(samples[0]);
    }
}

pub fn spawn_post_process_volume_at_camera(world: &mut World) {
    use shared::EditorVisibility;

    let translation = world
        .query_filtered::<&GlobalTransform, With<crate::camera::EditorCamera>>()
        .iter(world)
        .next()
        .map(|t| t.translation())
        .unwrap_or(Vec3::new(0.0, 2.0, 0.0));

    let snapshot = EditorEntitySnapshot {
        actor_id: None,
        actor_kind: ActorKind::PostProcessVolume,
        actor_name: None,
        name: Some("Post Process Volume".into()),
        transform: Transform::from_translation(translation),
        primitive: None,
        brush: None,
        static_mesh_renderer: None,
        skinned_mesh_renderer: None,
        material: None,
        material_override: None,
        rigid_body: None,
        collider: None,
        physics: None,
        light: None,
        animation_controller: None,
        audio_source: None,
        audio_listener: None,
        player_spawn: false,
        model: None,
        prefab: None,
        prefab_instance: None,
        weapon_spawn: None,
        trigger_volume: None,
        post_process_volume: Some(PostProcessVolumeDesc::default()),
        team_spawn: None,
        objective: None,

        navigation_bounds: None,

        navigation_obstacle: None,

        navigation_area: None,

        navigation_link: None,
        hierarchy_sibling_index: 0,
        editor_visibility: EditorVisibility::default(),
        inspector_order: None,
        component_states: None,
        children: Vec::new(),
    };
    spawn_with_history(world, snapshot);
}
