//! Dock layout persistence in user preferences.

use bevy::prelude::*;
use egui_dock::{DockState, NodeIndex};
use serde::{Deserialize, Serialize};

use crate::project_io::{write_user_preferences, UserPreferences};

use super::dock_tabs::{normalize_unified_viewport_tabs, PanelNodes};
use super::EditorTab;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DockLayoutSnapshot {
    pub dock_state: DockState<EditorTab>,
    pub main_view_node: usize,
    #[serde(default)]
    pub panel_nodes: Option<PanelNodes>,
}

#[derive(Resource, Default)]
pub struct LayoutSaveTimer {
    pub seconds_until_save: f32,
    pub last_serialized: Option<String>,
}

const SAVE_DEBOUNCE_SECS: f32 = 1.0;

pub fn default_dock_layout() -> DockLayoutSnapshot {
    let mut dock_state = DockState::new(vec![EditorTab::Viewport]);
    let tree = dock_state.main_surface_mut();
    let [main_view_node, inspector_node] =
        tree.split_right(NodeIndex::root(), 0.76, vec![EditorTab::Inspector]);
    let [main_view_node, hierarchy_node] =
        tree.split_left(main_view_node, 0.2, vec![EditorTab::Hierarchy]);
    let [main_view_node, bottom_node] = tree.split_below(
        main_view_node,
        0.88,
        vec![EditorTab::AssetBrowser, EditorTab::MaterialLibrary],
    );

    let panel_nodes = PanelNodes {
        main_view: main_view_node.0,
        hierarchy: hierarchy_node.0,
        inspector: inspector_node.0,
        bottom: bottom_node.0,
    };

    DockLayoutSnapshot {
        dock_state,
        main_view_node: main_view_node.0,
        panel_nodes: Some(panel_nodes),
    }
}

pub fn load_dock_layout(prefs: &UserPreferences) -> DockLayoutSnapshot {
    let Some(text) = prefs.dock_layout.as_ref() else {
        return default_dock_layout();
    };
    let mut snapshot: DockLayoutSnapshot =
        ron::from_str(text).unwrap_or_else(|_| default_dock_layout());
    normalize_unified_viewport_tabs(&mut snapshot.dock_state);
    if snapshot.panel_nodes.is_none() {
        let fallback = PanelNodes {
            main_view: snapshot.main_view_node,
            hierarchy: snapshot.main_view_node,
            inspector: snapshot.main_view_node,
            bottom: snapshot.main_view_node,
        };
        snapshot.panel_nodes = Some(PanelNodes::discover(&snapshot.dock_state, fallback));
    }
    snapshot
}

pub fn serialize_dock_layout(
    dock_state: &DockState<EditorTab>,
    main_view_node: NodeIndex,
    panel_nodes: PanelNodes,
) -> String {
    let snapshot = DockLayoutSnapshot {
        dock_state: dock_state.clone(),
        main_view_node: main_view_node.0,
        panel_nodes: Some(panel_nodes),
    };
    ron::ser::to_string(&snapshot).unwrap_or_default()
}

pub fn mark_layout_dirty_if_changed(
    world: &mut World,
    dock_state: &DockState<EditorTab>,
    main_view_node: NodeIndex,
    panel_nodes: PanelNodes,
) {
    let serialized = serialize_dock_layout(dock_state, main_view_node, panel_nodes);
    let mut timer = world.resource_mut::<LayoutSaveTimer>();
    if timer.last_serialized.as_ref() == Some(&serialized) {
        return;
    }
    timer.last_serialized = Some(serialized);
    timer.seconds_until_save = SAVE_DEBOUNCE_SECS;
    world
        .resource_mut::<crate::project_io::ProjectWorkspace>()
        .layout_dirty = true;
}

pub fn tick_layout_save(world: &mut World, dt: f32) {
    let mut timer = world.resource_mut::<LayoutSaveTimer>();
    if timer.seconds_until_save <= 0.0 {
        return;
    }
    timer.seconds_until_save -= dt;
    if timer.seconds_until_save > 0.0 {
        return;
    }

    let Some(serialized) = timer.last_serialized.clone() else {
        return;
    };

    let mut prefs = world.resource_mut::<UserPreferences>();
    prefs.dock_layout = Some(serialized);
    if let Err(err) = write_user_preferences(&prefs) {
        warn!("Failed to save editor layout: {err}");
    } else {
        world
            .resource_mut::<crate::project_io::ProjectWorkspace>()
            .layout_dirty = false;
    }
}

pub fn reset_and_save_layout(world: &mut World) -> DockLayoutSnapshot {
    let layout = default_dock_layout();
    let serialized = ron::ser::to_string(&layout).unwrap_or_default();

    {
        let mut prefs = world.resource_mut::<UserPreferences>();
        prefs.dock_layout = Some(serialized.clone());
        let _ = write_user_preferences(&prefs);
    }

    let mut timer = world.resource_mut::<LayoutSaveTimer>();
    timer.last_serialized = Some(serialized);
    timer.seconds_until_save = 0.0;
    world
        .resource_mut::<crate::project_io::ProjectWorkspace>()
        .layout_dirty = false;

    layout
}
