//! Detailed diagnostics (relocated from Status dock tab).

use bevy::prelude::*;
use bevy_egui::egui;
use shared::{
    brush_math::{validate_brush, BrushDiagnosticSeverity},
    ActorName, BrushDesc, LevelObject,
};

use crate::assets::EditorAssets;
use crate::diagnostics_bundle::export_diagnostic_bundle;
use crate::history::set_brush_with_history;
use crate::history::EditorHistory;
use crate::project_io::ProjectWorkspace;
use crate::scene_io::{SceneIo, SceneIoEventSeverity};
use crate::selection::SelectedEntity;
use crate::ui::UiState;

#[derive(Resource, Default)]
pub struct DiagnosticsPanel {
    pub open: bool,
    pub bundle_status: Option<String>,
    pub project_validation: Option<scene::ProjectValidationReport>,
}

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

pub fn diagnostics_ui(world: &mut World, ui: &mut egui::Ui) {
    let (scene_label, dirty, status, scene_events) = {
        let scene_io = world.resource::<SceneIo>();
        (
            scene_io.active_path_label(),
            scene_io.dirty,
            scene_io.status.clone(),
            scene_io.events.iter().rev().cloned().collect::<Vec<_>>(),
        )
    };
    let dirty = if dirty { " (dirty)" } else { "" };
    ui.label(format!("Scene: {scene_label}{dirty}"));
    ui.label(format!("Status: {status}"));
    ui.label(format!(
        "History: {}",
        world.resource::<EditorHistory>().status
    ));
    ui.label(format!(
        "Assets: {}",
        world.resource::<EditorAssets>().status
    ));

    let workspace = world.resource::<ProjectWorkspace>();
    ui.label(format!("Project root: {}", workspace.root));
    ui.label(format!(
        "Project settings: {}{}",
        workspace.settings_path,
        if workspace.settings_dirty {
            " (dirty)"
        } else {
            ""
        }
    ));
    if workspace.layout_dirty {
        ui.small("Dock layout modified (unsaved)");
    }

    let mut level_query = world.query_filtered::<Entity, With<LevelObject>>();
    let count = level_query.iter(world).count();
    ui.label(format!("Level objects: {count}"));

    ui.separator();
    ui.horizontal(|ui| {
        ui.strong("Project validation");
        if ui.button("Validate Project").clicked() {
            let root = world.resource::<ProjectWorkspace>().root.clone();
            let report = scene::validate_project(std::path::Path::new(&root));
            let summary = format!(
                "Project validation: {} dependencies, {} findings, {} blocking errors",
                report.dependencies.len(),
                report.findings.len(),
                report.blocking_error_count()
            );
            world.resource_mut::<SceneIo>().set_status(summary);
            world.resource_mut::<DiagnosticsPanel>().project_validation = Some(report);
        }
    });
    if let Some(report) = world
        .resource::<DiagnosticsPanel>()
        .project_validation
        .as_ref()
        .cloned()
    {
        let color = if report.is_release_ready() {
            egui::Color32::from_rgb(125, 210, 145)
        } else {
            egui::Color32::from_rgb(255, 137, 129)
        };
        ui.colored_label(
            color,
            format!(
                "{} dependencies, {} blocking errors",
                report.dependencies.len(),
                report.blocking_error_count()
            ),
        );
        for finding in report.findings.iter().take(12) {
            let actor = finding
                .owner_actor_id
                .as_deref()
                .map(|actor| format!(" / {actor}"))
                .unwrap_or_default();
            ui.horizontal_wrapped(|ui| {
                ui.monospace(format!("{}{}", finding.source_path, actor));
                ui.label(&finding.message);
                if let Some(actor_id) = finding.owner_actor_id.as_deref() {
                    if ui.small_button("Select").clicked() {
                        let mut query = world.query::<(Entity, &shared::ActorId)>();
                        if let Some(entity) = query
                            .iter(world)
                            .find_map(|(entity, id)| (id.0 == actor_id).then_some(entity))
                        {
                            world.resource_mut::<SelectedEntity>().0 = Some(entity);
                            world
                                .resource_mut::<UiState>()
                                .selected_entities
                                .select_replace(entity);
                        }
                    }
                }
            });
        }
        if report.findings.len() > 12 {
            ui.small(format!(
                "{} additional findings",
                report.findings.len() - 12
            ));
        }
    }

    ui.separator();
    ui.horizontal(|ui| {
        ui.strong("Support bundle");
        if ui.button("Export Diagnostic Bundle").clicked() {
            let status = match export_diagnostic_bundle(world) {
                Ok(path) => format!("Exported {}", path.display()),
                Err(error) => format!("Export failed: {error}"),
            };
            world.resource_mut::<DiagnosticsPanel>().bundle_status = Some(status);
        }
    });
    ui.small("Allowlisted metadata only; scene and asset contents, environment values, credentials, and modal state are excluded.");
    if let Some(status) = world
        .resource::<DiagnosticsPanel>()
        .bundle_status
        .as_deref()
    {
        ui.monospace(status);
    }

    ui.separator();
    ui.horizontal(|ui| {
        ui.strong("Scene I/O log");
        if ui
            .add_enabled(!scene_events.is_empty(), egui::Button::new("Clear"))
            .clicked()
        {
            world.resource_mut::<SceneIo>().clear_events();
        }
    });
    if scene_events.is_empty() {
        ui.small("No scene I/O operations recorded this session.");
    } else {
        egui::ScrollArea::vertical()
            .id_salt("scene_io_diagnostics_log")
            .max_height(180.0)
            .show(ui, |ui| {
                for event in scene_events {
                    let color = match event.severity {
                        SceneIoEventSeverity::Info => ui.visuals().weak_text_color(),
                        SceneIoEventSeverity::Error => egui::Color32::from_rgb(244, 110, 105),
                    };
                    ui.horizontal_wrapped(|ui| {
                        ui.colored_label(color, format!("#{:02}", event.id));
                        ui.colored_label(color, event.message);
                    });
                }
            });
    }

    ui.separator();
    ui.small("Native Bevy scenes are saved to assets/levels/*.scn.ron");
    ui.small("BRP is enabled on the editor for external tooling.");
}

pub fn brush_diagnostics_window(world: &mut World, ctx: &egui::Context, open: &mut bool) {
    if !*open {
        return;
    }
    egui::Window::new("Brush Diagnostics")
        .open(open)
        .default_width(520.0)
        .show(ctx, |ui| {
            brush_diagnostics_ui(world, ui);
        });
}

fn brush_diagnostics_ui(world: &mut World, ui: &mut egui::Ui) {
    let rows = brush_diagnostic_rows(world);
    let invalid_count = rows.iter().filter(|row| row.error_count > 0).count();
    ui.label(format!(
        "{} brush actor(s), {} invalid",
        rows.len(),
        invalid_count
    ));
    ui.separator();
    if rows.is_empty() {
        ui.small("No brush actors in the current scene.");
        return;
    }

    for row in rows {
        ui.horizontal_wrapped(|ui| {
            let color = if row.error_count > 0 {
                egui::Color32::from_rgb(255, 137, 129)
            } else if row.warning_count > 0 {
                egui::Color32::from_rgb(255, 190, 110)
            } else {
                egui::Color32::from_rgb(125, 210, 145)
            };
            ui.colored_label(color, row.status_label());
            ui.label(row.name.as_str());
            if ui.button("Select").clicked() {
                world.resource_mut::<SelectedEntity>().0 = Some(row.entity);
                world
                    .resource_mut::<UiState>()
                    .selected_entities
                    .select_replace(row.entity);
            }
            if ui
                .add_enabled(row.error_count > 0, egui::Button::new("Reset Cube"))
                .clicked()
            {
                set_brush_with_history(world, row.entity, BrushDesc::default());
            }
        });
        if let Some(message) = row.first_message.as_ref() {
            ui.small(message);
        }
    }
}

#[derive(Debug)]
struct BrushDiagnosticRow {
    entity: Entity,
    name: String,
    error_count: usize,
    warning_count: usize,
    first_message: Option<String>,
}

impl BrushDiagnosticRow {
    fn status_label(&self) -> String {
        if self.error_count > 0 {
            format!("{} error(s)", self.error_count)
        } else if self.warning_count > 0 {
            format!("{} warning(s)", self.warning_count)
        } else {
            "valid".into()
        }
    }
}

fn brush_diagnostic_rows(world: &mut World) -> Vec<BrushDiagnosticRow> {
    let mut query =
        world.query_filtered::<(Entity, Option<&ActorName>, Option<&Name>, &BrushDesc), With<LevelObject>>();
    let mut rows: Vec<_> = query
        .iter(world)
        .map(|(entity, actor_name, name, brush)| {
            let report = validate_brush(brush);
            let error_count = report
                .diagnostics
                .iter()
                .filter(|diagnostic| diagnostic.severity == BrushDiagnosticSeverity::Error)
                .count();
            let warning_count = report
                .diagnostics
                .iter()
                .filter(|diagnostic| diagnostic.severity == BrushDiagnosticSeverity::Warning)
                .count();
            let first_message = report
                .diagnostics
                .first()
                .map(|diagnostic| diagnostic.message.clone());
            BrushDiagnosticRow {
                entity,
                name: actor_name
                    .map(|name| name.0.clone())
                    .or_else(|| name.map(|name| name.to_string()))
                    .unwrap_or_else(|| format!("{entity:?}")),
                error_count,
                warning_count,
                first_message,
            }
        })
        .collect();
    rows.sort_by(|a, b| {
        b.error_count
            .cmp(&a.error_count)
            .then(b.warning_count.cmp(&a.warning_count))
            .then(a.name.cmp(&b.name))
    });
    rows
}

pub fn diagnostics_window(world: &mut World, ctx: &egui::Context, open: &mut bool) {
    if !*open {
        return;
    }
    egui::Window::new("Diagnostics")
        .open(open)
        .default_width(420.0)
        .show(ctx, |ui| {
            diagnostics_ui(world, ui);
        });
}
