//! Top-level authored scene tabs and composition controls.

use std::path::{Path, PathBuf};

use bevy::prelude::*;
use bevy_egui::egui;
use bevy_inspector_egui::bevy_inspector::hierarchy::SelectedEntities;
use egui_phosphor_icons::icons;
use shared::{SceneComposition, SubsceneReference};

use crate::scene_io::{ComposedSceneMember, SceneIo, SceneIoRequest};
use crate::selection::SelectedEntity;

use super::selection_ops::focus_editor_camera_on_selection;
use super::theme::{BORDER, SELECTION, SELECTION_BG, TEXT, TEXT_DIM, TEXT_MUTED, WIDGET_BG};
use super::widgets::phosphor_icon;

pub fn scene_tabs_chrome(
    world: &mut World,
    ui: &mut egui::Ui,
    selected: &mut SelectedEntities,
    editing: bool,
) {
    let (tabs, active_tab) = {
        let io = world.resource::<SceneIo>();
        (io.tabs.clone(), io.active_tab)
    };
    let composition_editing = editing
        && !world
            .resource::<SceneIo>()
            .active_path
            .as_deref()
            .is_some_and(crate::scene_io::is_prefab_document);
    let composition_count = world.resource::<SceneComposition>().subscenes.len();
    let mut request = None;
    ui.spacing_mut().item_spacing.x = 3.0;
    ui.menu_button(
        super::tab_title(icons::STACK, &format!("Composition  {composition_count}")),
        |ui| composition_menu(world, ui, selected, composition_editing),
    )
    .response
    .on_hover_text(if composition_editing {
        "Scene composition"
    } else {
        "Composition is unavailable for prefab documents"
    });
    ui.separator();
    if ui
        .add_enabled(
            editing,
            egui::Button::new(phosphor_icon(icons::PLUS, 13.0))
                .frame(false)
                .min_size(egui::vec2(22.0, 20.0)),
        )
        .on_hover_text("New scene tab")
        .clicked()
    {
        request = Some(SceneIoRequest::New);
    }

    for (index, tab) in tabs.iter().enumerate().rev() {
        if ui
            .add_enabled(
                editing,
                egui::Button::new(phosphor_icon(icons::X, 11.0))
                    .frame(false)
                    .min_size(egui::vec2(18.0, 20.0)),
            )
            .on_hover_text("Close scene tab")
            .clicked()
        {
            request = Some(SceneIoRequest::CloseTab(index));
        }
        let active = index == active_tab;
        let label = if tab.dirty {
            format!("{} *", tab.label())
        } else {
            tab.label()
        };
        let text = egui::RichText::new(label)
            .color(if active { TEXT } else { TEXT_DIM })
            .strong();
        let response = ui.add_enabled(
            editing,
            egui::Button::new(text)
                .fill(if active { SELECTION_BG } else { WIDGET_BG })
                .stroke(egui::Stroke::new(
                    1.0,
                    if active { SELECTION } else { BORDER },
                ))
                .min_size(egui::vec2(128.0, 20.0)),
        );
        if response.clicked() && !active {
            request = Some(SceneIoRequest::SwitchTab(index));
        }
        if response.hovered() {
            let path = tab
                .path
                .as_deref()
                .map(Path::display)
                .map(|path| path.to_string())
                .unwrap_or_else(|| "Unsaved scene".to_string());
            response.on_hover_text(path);
        }
    }

    if !editing {
        ui.label(egui::RichText::new("Play mode").color(TEXT_MUTED));
    }

    if let Some(request) = request {
        world.resource_mut::<SceneIo>().request = Some(request);
    }
}

fn composition_menu(
    world: &mut World,
    ui: &mut egui::Ui,
    selected: &mut SelectedEntities,
    editing: bool,
) {
    ui.set_min_width(480.0);
    let mut composition = world.resource::<SceneComposition>().clone();
    ui.label(
        egui::RichText::new(format!("Scene ID  {}", composition.scene_id))
            .monospace()
            .size(11.0)
            .color(TEXT_MUTED),
    );
    ui.separator();

    let mut changed = false;
    let mut remove = None;
    let mut focus = None;
    if composition.subscenes.is_empty() {
        ui.label(egui::RichText::new("No composed subscenes").color(TEXT_DIM));
    }
    for (index, reference) in composition.subscenes.iter_mut().enumerate() {
        ui.horizontal(|ui| {
            let file_name = Path::new(&reference.path)
                .file_name()
                .and_then(|name| name.to_str())
                .unwrap_or(&reference.path);
            ui.label(egui::RichText::new(file_name).strong().color(TEXT));
            ui.label(
                egui::RichText::new(&reference.path)
                    .monospace()
                    .size(10.0)
                    .color(TEXT_MUTED),
            );
            ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                if ui
                    .add_enabled(
                        editing,
                        egui::Button::new(phosphor_icon(icons::TRASH, 14.0)).frame(false),
                    )
                    .on_hover_text("Remove subscene reference")
                    .clicked()
                {
                    remove = Some(index);
                }
                if ui
                    .add_enabled(reference.visible, egui::Button::new("Focus"))
                    .clicked()
                {
                    focus = Some(reference.id.clone());
                    ui.close();
                }
                if ui
                    .add_enabled(
                        editing,
                        egui::Checkbox::new(&mut reference.locked, "Locked"),
                    )
                    .changed()
                {
                    changed = true;
                }
                if ui
                    .add_enabled(
                        editing,
                        egui::Checkbox::new(&mut reference.visible, "Loaded"),
                    )
                    .changed()
                {
                    changed = true;
                }
            });
        });
        ui.add_space(4.0);
    }
    if let Some(index) = remove {
        composition.subscenes.remove(index);
        changed = true;
    }

    ui.separator();
    if ui
        .add_enabled(editing, egui::Button::new("Add Subscene..."))
        .clicked()
    {
        match choose_subscene(world, &composition) {
            Ok(Some(reference)) => {
                composition.subscenes.push(reference);
                changed = true;
            }
            Ok(None) => {}
            Err(error) => world
                .resource_mut::<SceneIo>()
                .set_status(format!("Add subscene failed: {error}")),
        }
    }

    if changed {
        crate::history::set_scene_composition_with_history(world, composition);
    }
    if let Some(reference_id) = focus {
        focus_subscene(world, selected, &reference_id);
    }
}

fn choose_subscene(
    world: &World,
    composition: &SceneComposition,
) -> Result<Option<SubsceneReference>, String> {
    let project_root = PathBuf::from(&world.resource::<crate::project_io::ProjectWorkspace>().root)
        .canonicalize()
        .map_err(|error| format!("could not resolve project root: {error}"))?;
    let Some(path) = rfd::FileDialog::new()
        .set_directory(project_root.join("assets/levels"))
        .add_filter("Bevy scene", &["scn.ron", "ron"])
        .pick_file()
    else {
        return Ok(None);
    };
    let path = path
        .canonicalize()
        .map_err(|error| format!("could not resolve {}: {error}", path.display()))?;
    let relative = path.strip_prefix(&project_root).map_err(|_| {
        format!(
            "{} is outside project root {}",
            path.display(),
            project_root.display()
        )
    })?;
    scene::validate_level_file(&path)?;
    let relative = relative.to_string_lossy().replace('\\', "/");
    if composition
        .subscenes
        .iter()
        .any(|reference| reference.path == relative)
    {
        return Err(format!("{relative} is already composed"));
    }
    Ok(Some(SubsceneReference {
        id: uuid::Uuid::new_v4().to_string(),
        path: relative,
        visible: true,
        locked: true,
    }))
}

fn focus_subscene(world: &mut World, selected: &mut SelectedEntities, reference_id: &str) {
    let mut query = world.query::<(Entity, &ComposedSceneMember)>();
    let entity = query.iter(world).find_map(|(entity, member)| {
        (member.reference_id == reference_id
            || member.reference_id.starts_with(&format!("{reference_id}/")))
        .then_some(entity)
    });
    let Some(entity) = entity else {
        world
            .resource_mut::<SceneIo>()
            .set_status("Subscene is not loaded or contains no authored actors");
        return;
    };
    selected.clear();
    selected.select_replace(entity);
    world.resource_mut::<SelectedEntity>().0 = Some(entity);
    focus_editor_camera_on_selection(world);
    world
        .resource_mut::<SceneIo>()
        .set_status(format!("Focused composed subscene {reference_id}"));
}
