use crate::ecs::world::World;
use nalgebra_glm::{Vec2, Vec4};

use crate::ecs::ui::builder::UiTreeBuilder;
use crate::ecs::ui::components::*;
use crate::ecs::ui::state::UiStateTrait;
use crate::ecs::ui::types::Anchor;
use crate::ecs::ui::units::Ab;

use super::tile_builder::TileBuilder;

use crate::prelude::*;
pub fn ui_tile_add_pane(
    world: &mut World,
    container: freecs::Entity,
    title: &str,
) -> Option<(TileId, freecs::Entity)> {
    let content_entity = spawn_tile_pane_entity(world, container);

    let data = world.ui.get_ui_tile_container_mut(container)?;

    let pane = TileNode::Pane {
        content_entity,
        title: title.to_string(),
    };
    let pane_id = data.alloc(pane);

    let mut current = data.root;
    let target_tabs = loop {
        match data.get(current) {
            Some(TileNode::Tabs { .. }) => break Some(current),
            Some(TileNode::Split { children, .. }) => current = children[0],
            _ => break None,
        }
    };
    if let Some(tabs_id) = target_tabs
        && let Some(TileNode::Tabs { panes, .. }) = data.get_mut(tabs_id)
    {
        panes.push(pane_id);
    }

    Some((pane_id, content_entity))
}

pub fn ui_tile_add_pane_to(
    world: &mut World,
    container: freecs::Entity,
    tabs_id: TileId,
    title: &str,
) -> Option<(TileId, freecs::Entity)> {
    let (pane_id, content_entity) = ui_tile_add_pane(world, container, title)?;

    let data = world.ui.get_ui_tile_container_mut(container)?;

    if let Some(parent_tabs_id) = data.find_parent_tabs(pane_id)
        && parent_tabs_id != tabs_id
        && let Some(TileNode::Tabs { panes, active }) = data.get_mut(parent_tabs_id)
    {
        panes.retain(|id| *id != pane_id);
        if *active >= panes.len() && !panes.is_empty() {
            *active = panes.len() - 1;
        }
    }

    if let Some(TileNode::Tabs { panes, .. }) = data.get_mut(tabs_id)
        && !panes.contains(&pane_id)
    {
        panes.push(pane_id);
    }

    Some((pane_id, content_entity))
}

pub fn ui_tile_split(
    world: &mut World,
    container: freecs::Entity,
    target: TileId,
    direction: SplitDirection,
    ratio: f32,
    title: &str,
) -> Option<(TileId, freecs::Entity)> {
    let (pane_id, content_entity) = ui_tile_add_pane(world, container, title)?;

    let data = world.ui.get_ui_tile_container_mut(container)?;

    if let Some(parent_tabs_id) = data.find_parent_tabs(pane_id)
        && let Some(TileNode::Tabs { panes, .. }) = data.get_mut(parent_tabs_id)
    {
        panes.retain(|id| *id != pane_id);
    }

    let new_tabs = TileNode::Tabs {
        panes: vec![pane_id],
        active: 0,
    };
    let new_tabs_id = data.alloc(new_tabs);

    let old_node = data.tiles[target.0].take()?;
    let old_id = data.alloc(old_node);

    let split = TileNode::Split {
        direction,
        ratio,
        children: [old_id, new_tabs_id],
    };
    data.tiles[target.0] = Some(split);

    Some((pane_id, content_entity))
}

pub fn ui_tile_reset_to_panes(
    world: &mut World,
    container: freecs::Entity,
    panes: &[(freecs::Entity, &str)],
) -> Option<Vec<TileId>> {
    let data = world.ui.get_ui_tile_container_mut(container)?;
    for tile in data.tiles.iter_mut() {
        *tile = None;
    }
    data.next_free.clear();
    let mut pane_ids = Vec::with_capacity(panes.len());
    for (content_entity, title) in panes {
        let pane = TileNode::Pane {
            content_entity: *content_entity,
            title: title.to_string(),
        };
        pane_ids.push(data.alloc(pane));
    }
    let root_id = data.alloc(TileNode::Tabs {
        panes: pane_ids.clone(),
        active: 0,
    });
    data.root = root_id;
    Some(pane_ids)
}

pub fn ui_tile_wrap_pane_in_tabs(
    world: &mut World,
    container: freecs::Entity,
    pane_id: TileId,
) -> Option<TileId> {
    let data = world.ui.get_ui_tile_container_mut(container)?;
    if !matches!(data.get(pane_id), Some(TileNode::Pane { .. })) {
        return None;
    }
    if data.find_parent_tabs(pane_id).is_some() {
        return None;
    }
    let new_tabs_id = data.alloc(TileNode::Tabs {
        panes: vec![pane_id],
        active: 0,
    });
    if let Some((parent_split_id, child_index)) = data.find_parent_split(pane_id) {
        if let Some(TileNode::Split { children, .. }) = data.get_mut(parent_split_id) {
            children[child_index] = new_tabs_id;
        }
    } else if data.root == pane_id {
        data.root = new_tabs_id;
    }
    Some(new_tabs_id)
}

pub fn ui_tile_remove(world: &mut World, container: freecs::Entity, tile_id: TileId) {
    let content_to_hide = if let Some(data) = world.ui.get_ui_tile_container(container) {
        let pane_count = data
            .tiles
            .iter()
            .filter(|t| matches!(t, Some(TileNode::Pane { .. })))
            .count();
        if pane_count < 2 {
            return;
        }
        if let Some(TileNode::Pane { content_entity, .. }) = data.get(tile_id) {
            Some(*content_entity)
        } else {
            None
        }
    } else {
        return;
    };

    if let Some(content) = content_to_hide
        && let Some(node) = world.ui.get_ui_layout_node_mut(content)
    {
        node.visible = false;
    }

    let Some(data) = world.ui.get_ui_tile_container_mut(container) else {
        return;
    };

    if let Some(tabs_id) = data.find_parent_tabs(tile_id) {
        if let Some(TileNode::Tabs { panes, active }) = data.get_mut(tabs_id) {
            panes.retain(|id| *id != tile_id);
            if *active >= panes.len() && !panes.is_empty() {
                *active = panes.len() - 1;
            }
        }
        data.free(tile_id);

        let remaining = if let Some(TileNode::Tabs { panes, .. }) = data.get(tabs_id) {
            panes.len()
        } else {
            0
        };

        if remaining == 0 {
            if let Some((parent_split_id, child_index)) = data.find_parent_split(tabs_id) {
                let sibling_index = 1 - child_index;
                let sibling_id =
                    if let Some(TileNode::Split { children, .. }) = data.get(parent_split_id) {
                        children[sibling_index]
                    } else {
                        return;
                    };

                let sibling_node = data.tiles[sibling_id.0].take();
                data.tiles[parent_split_id.0] = sibling_node;
                data.free(tabs_id);
                data.free(sibling_id);
            } else {
                data.free(tabs_id);
            }
        }
    }
}

pub fn ui_tile_pane_content(
    world: &World,
    container: freecs::Entity,
    tile_id: TileId,
) -> Option<freecs::Entity> {
    let data = world.ui.get_ui_tile_container(container)?;
    if let Some(TileNode::Pane { content_entity, .. }) = data.get(tile_id) {
        Some(*content_entity)
    } else {
        None
    }
}

pub fn ui_tile_pane_title(
    world: &World,
    container: freecs::Entity,
    pane_id: TileId,
) -> Option<String> {
    let data = world.ui.get_ui_tile_container(container)?;
    if let Some(TileNode::Pane { title, .. }) = data.get(pane_id) {
        Some(title.clone())
    } else {
        None
    }
}

pub fn ui_tile_active_pane(world: &World, container: freecs::Entity, pane_id: TileId) -> bool {
    let Some(data) = world.ui.get_ui_tile_container(container) else {
        return false;
    };
    if let Some(tabs_id) = data.find_parent_tabs(pane_id)
        && let Some(TileNode::Tabs { panes, active }) = data.get(tabs_id)
        && panes.get(*active) == Some(&pane_id)
    {
        return true;
    }
    false
}

pub fn ui_tile_tab_activated(world: &World, container: freecs::Entity) -> Option<TileId> {
    for event in &world.resources.retained_ui.frame.events {
        if let crate::ecs::ui::resources::UiEvent::TileTabActivated {
            container: event_container,
            pane_id,
        } = event
            && *event_container == container
        {
            return Some(*pane_id);
        }
    }
    None
}

pub fn ui_tile_tab_closed(world: &World, container: freecs::Entity) -> Option<(TileId, String)> {
    for event in &world.resources.retained_ui.frame.events {
        if let crate::ecs::ui::resources::UiEvent::TileTabClosed {
            container: event_container,
            pane_id,
            title,
        } = event
            && *event_container == container
        {
            return Some((*pane_id, title.clone()));
        }
    }
    None
}

pub fn ui_tile_splitter_moved(world: &World, container: freecs::Entity) -> Option<(TileId, f32)> {
    for event in &world.resources.retained_ui.frame.events {
        if let crate::ecs::ui::resources::UiEvent::TileSplitterMoved {
            container: event_container,
            split_id,
            ratio,
        } = event
            && *event_container == container
        {
            return Some((*split_id, *ratio));
        }
    }
    None
}

pub fn ui_tile_set_active(world: &mut World, container: freecs::Entity, pane_id: TileId) {
    let Some(data) = world.ui.get_ui_tile_container_mut(container) else {
        return;
    };
    if let Some(tabs_id) = data.find_parent_tabs(pane_id)
        && let Some(TileNode::Tabs { panes, active }) = data.get_mut(tabs_id)
        && let Some(index) = panes.iter().position(|id| *id == pane_id)
    {
        *active = index;
    }
}

pub fn ui_tile_save_layout(
    world: &World,
    container: freecs::Entity,
) -> Option<crate::ecs::ui::components::TileLayout> {
    use crate::ecs::ui::components::{TileLayout, TileLayoutNode};
    let data = world.ui.get_ui_tile_container(container)?;
    let nodes = data
        .tiles
        .iter()
        .map(|tile| {
            tile.as_ref().map(|node| match node {
                TileNode::Pane { title, .. } => TileLayoutNode::Pane {
                    title: title.clone(),
                },
                TileNode::Split {
                    direction,
                    ratio,
                    children,
                } => TileLayoutNode::Split {
                    direction: *direction,
                    ratio: *ratio,
                    children: *children,
                },
                TileNode::Tabs { panes, active } => TileLayoutNode::Tabs {
                    panes: panes.clone(),
                    active: *active,
                },
            })
        })
        .collect();
    Some(TileLayout {
        nodes,
        root: data.root,
    })
}

pub fn ui_tile_load_layout(
    world: &mut World,
    container: freecs::Entity,
    layout: &crate::ecs::ui::components::TileLayout,
) -> Vec<(TileId, freecs::Entity)> {
    use crate::ecs::ui::components::TileLayoutNode;

    let old_pane_entities: Vec<freecs::Entity> =
        if let Some(data) = world.ui.get_ui_tile_container(container) {
            data.tiles
                .iter()
                .filter_map(|tile| {
                    if let Some(TileNode::Pane { content_entity, .. }) = tile {
                        Some(*content_entity)
                    } else {
                        None
                    }
                })
                .collect()
        } else {
            return Vec::new();
        };

    for entity in &old_pane_entities {
        ui_despawn_node(world, *entity);
    }

    let mut pane_mappings = Vec::new();
    let mut new_tiles: Vec<Option<TileNode>> = Vec::with_capacity(layout.nodes.len());
    for layout_node in &layout.nodes {
        match layout_node {
            Some(TileLayoutNode::Pane { title }) => {
                let content_entity = spawn_tile_pane_entity(world, container);
                let tile_id = TileId(new_tiles.len());
                pane_mappings.push((tile_id, content_entity));
                new_tiles.push(Some(TileNode::Pane {
                    content_entity,
                    title: title.clone(),
                }));
            }
            Some(TileLayoutNode::Split {
                direction,
                ratio,
                children,
            }) => {
                new_tiles.push(Some(TileNode::Split {
                    direction: *direction,
                    ratio: *ratio,
                    children: *children,
                }));
            }
            Some(TileLayoutNode::Tabs { panes, active }) => {
                new_tiles.push(Some(TileNode::Tabs {
                    panes: panes.clone(),
                    active: *active,
                }));
            }
            None => {
                new_tiles.push(None);
            }
        }
    }

    let mut next_free = Vec::new();
    for (index, tile) in new_tiles.iter().enumerate() {
        if tile.is_none() {
            next_free.push(index);
        }
    }

    let rects_len = new_tiles.len();
    if let Some(data) = world.ui.get_ui_tile_container_mut(container) {
        data.tiles = new_tiles;
        data.root = layout.root;
        data.next_free = next_free;
        data.rects
            .resize(rects_len, crate::ecs::ui::types::Rect::default());
        data.dragging_splitter = None;
        data.pending_tab_drag = None;
        data.dragging_tab = None;
        data.drop_preview = None;
        data.hovered_close = None;
    }

    ui_mark_children_dirty(world);

    pane_mappings
}

pub fn spawn_tile_pane_entity(world: &mut World, container: freecs::Entity) -> freecs::Entity {
    use crate::ecs::ui::layout_types::{FlowAlignment, FlowDirection, FlowLayout};
    use crate::ecs::ui::state::UiBase;

    let theme = world.resources.retained_ui.theme_state.active_theme();
    let panel_color = theme.panel_color;

    let content_entity = world.spawn();
    world
        .core
        .add_components(content_entity, crate::ecs::world::PARENT);
    world.ui.add_components(
        content_entity,
        crate::ecs::world::UI_LAYOUT_NODE
            | crate::ecs::world::UI_NODE_COLOR
            | crate::ecs::world::UI_NODE_CONTENT
            | crate::ecs::world::UI_STATE_WEIGHTS,
    );

    if let Some(parent) = world.core.get_parent_mut(content_entity) {
        *parent = crate::ecs::transform::components::Parent(Some(container));
    }
    if let Some(node) = world.ui.get_ui_layout_node_mut(content_entity) {
        *node = crate::ecs::ui::components::UiLayoutNode::default();
        node.base_layout = Some(crate::ecs::ui::layout_types::UiLayoutType::Window(
            crate::ecs::ui::layout_types::WindowLayout {
                position: Ab(Vec2::new(0.0, 0.0)).into(),
                size: Ab(Vec2::new(100.0, 100.0)).into(),
                anchor: Anchor::TopLeft,
            },
        ));
        node.depth = crate::ecs::ui::components::UiDepthMode::Add(0.0);
        node.clip_content = true;
        node.flow_layout = Some(FlowLayout {
            direction: FlowDirection::Vertical,
            padding: 4.0,
            spacing: 4.0,
            alignment: FlowAlignment::Start,
            cross_alignment: FlowAlignment::Start,
            wrap: false,
        });
    }
    if let Some(color) = world.ui.get_ui_node_color_mut(content_entity) {
        color.colors[UiBase::INDEX] = Some(panel_color);
    }
    if let Some(content) = world.ui.get_ui_node_content_mut(content_entity) {
        *content = crate::ecs::ui::components::UiNodeContent::Rect {
            corner_radius: 0.0,
            border_width: 0.0,
            border_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
        };
    }

    ui_mark_children_dirty(world);

    content_entity
}

pub fn build_tiles(
    world: &mut World,
    container: freecs::Entity,
    f: impl FnOnce(&mut TileBuilder<'_, '_>),
) {
    let mut tree = UiTreeBuilder::from_parent(world, container);
    tree.build_tiles(container, f);
}
