//! Dock tab open/focus helpers and default panel node placement.

use egui_dock::{DockState, Node, NodeIndex, NodePath, SurfaceIndex, TabIndex, TabPath, Tree};
use serde::{Deserialize, Serialize};

use super::EditorTab;

/// Preferred leaf nodes for re-opening closed panels (updated when layout is built or loaded).
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct PanelNodes {
    pub main_view: usize,
    pub hierarchy: usize,
    pub inspector: usize,
    pub bottom: usize,
}

impl PanelNodes {
    pub fn main_view(&self) -> NodeIndex {
        NodeIndex::from(self.main_view)
    }

    pub fn hierarchy(&self) -> NodeIndex {
        NodeIndex::from(self.hierarchy)
    }

    pub fn inspector(&self) -> NodeIndex {
        NodeIndex::from(self.inspector)
    }

    pub fn bottom(&self) -> NodeIndex {
        NodeIndex::from(self.bottom)
    }

    pub fn preferred_node(&self, tab: &EditorTab) -> NodeIndex {
        match tab {
            EditorTab::Viewport | EditorTab::GameView => self.main_view(),
            EditorTab::Hierarchy => self.hierarchy(),
            EditorTab::Inspector => self.inspector(),
            EditorTab::AssetBrowser
            | EditorTab::MaterialLibrary
            | EditorTab::Build
            | EditorTab::Toolbar => self.bottom(),
        }
    }

    /// Scan the main surface for open tabs and refresh node indices (handles user drag/split).
    pub fn discover(dock: &DockState<EditorTab>, fallback: Self) -> Self {
        let mut nodes = fallback;
        for (path, tab) in dock.iter_all_tabs() {
            if path.surface != SurfaceIndex::main() {
                continue;
            }
            match tab {
                EditorTab::Viewport | EditorTab::GameView => nodes.main_view = path.node.0,
                EditorTab::Hierarchy => nodes.hierarchy = path.node.0,
                EditorTab::Inspector => nodes.inspector = path.node.0,
                EditorTab::AssetBrowser
                | EditorTab::MaterialLibrary
                | EditorTab::Build
                | EditorTab::Toolbar => nodes.bottom = path.node.0,
            }
        }
        nodes
    }
}

pub const PANEL_TABS: [EditorTab; 6] = [
    EditorTab::Viewport,
    EditorTab::Hierarchy,
    EditorTab::Inspector,
    EditorTab::AssetBrowser,
    EditorTab::MaterialLibrary,
    EditorTab::Build,
];

pub fn tab_label(tab: &EditorTab) -> &'static str {
    match tab {
        EditorTab::Viewport | EditorTab::GameView => "Viewport",
        EditorTab::Hierarchy => "Hierarchy",
        EditorTab::Inspector => "Inspector",
        EditorTab::AssetBrowser => "Asset Browser",
        EditorTab::MaterialLibrary => "Material Library",
        EditorTab::Build => "Build",
        EditorTab::Toolbar => "Toolbar (legacy)",
    }
}

pub fn tab_is_open(dock: &DockState<EditorTab>, tab: &EditorTab) -> bool {
    dock.find_main_surface_tab(tab).is_some()
}

/// Opens `tab` on the main surface if missing, then focuses it.
pub fn open_and_focus_tab(
    dock: &mut DockState<EditorTab>,
    tab: EditorTab,
    panels: &PanelNodes,
) -> (NodeIndex, TabIndex) {
    let (node, tab_idx) = if let Some(loc) = dock.find_main_surface_tab(&tab) {
        loc
    } else {
        let tree = dock.main_surface_mut();
        let node = panels.preferred_node(&tab);
        let tab_idx = push_tab_to_node(tree, node, tab);
        (node, tab_idx)
    };
    let node_path = NodePath::new(SurfaceIndex::main(), node);
    let _ = dock.set_active_tab(TabPath::new(SurfaceIndex::main(), node, tab_idx));
    dock.set_focused_node_and_surface(node_path);
    (node, tab_idx)
}

pub fn ensure_viewport_for_play(dock: &mut DockState<EditorTab>, panels: &PanelNodes) {
    open_and_focus_tab(dock, EditorTab::Viewport, panels);
}

/// Migrates old saved layouts by replacing/removing legacy Game View tabs.
pub fn normalize_unified_viewport_tabs(dock: &mut DockState<EditorTab>) {
    let has_viewport = dock
        .iter_all_tabs()
        .any(|(_, tab)| matches!(tab, EditorTab::Viewport));

    if !has_viewport {
        if let Some((_, tab)) = dock
            .iter_all_tabs_mut()
            .find(|(_, tab)| matches!(tab, EditorTab::GameView))
        {
            *tab = EditorTab::Viewport;
        }
    }

    let mut seen_viewport = false;
    dock.retain_tabs(|tab| match tab {
        EditorTab::GameView => false,
        EditorTab::Viewport => {
            if seen_viewport {
                false
            } else {
                seen_viewport = true;
                true
            }
        }
        _ => true,
    });

    if !seen_viewport {
        dock.main_surface_mut()
            .push_to_first_leaf(EditorTab::Viewport);
    }
}

fn push_tab_to_node(tree: &mut Tree<EditorTab>, node_index: NodeIndex, tab: EditorTab) -> TabIndex {
    match &mut tree[node_index] {
        Node::Leaf(leaf) => {
            let tab_idx = TabIndex(leaf.tabs.len());
            leaf.tabs.push(tab);
            leaf.active = tab_idx;
            tab_idx
        }
        Node::Empty => {
            tree[node_index] = Node::leaf(tab);
            TabIndex(0)
        }
        _ => {
            let kind = tab.clone();
            tree.push_to_first_leaf(tab);
            tree.find_tab(&kind).expect("tab was just pushed").1
        }
    }
}
