use std::sync::{
    Arc,
    Mutex,
};
use accesskit::{
    Action,
    DefaultActionVerb,
    Node,
    NodeBuilder,
    Rect,
    Role,
    Tree,
    TreeUpdate,
};
use freya_node_state::AccessibilityNodeState;
use torin::prelude::LayoutNode;
use crate::{
    accessibility::*,
    dom::DioxusNode,
};
pub type SharedAccessibilityManager = Arc<Mutex<AccessibilityManager>>;
pub const ACCESSIBILITY_ROOT_ID: AccessibilityId = AccessibilityId(0);
pub struct AccessibilityManager {
    pub nodes: Vec<(AccessibilityId, Node)>,
    pub focused_id: AccessibilityId,
}
impl AccessibilityManager {
    pub fn new(focused_id: AccessibilityId) -> Self {
        Self {
            focused_id,
            nodes: Vec::default(),
        }
    }
    pub fn wrap(self) -> SharedAccessibilityManager {
        Arc::new(Mutex::new(self))
    }
    pub fn clear(&mut self) {
        self.nodes.clear();
    }
    pub fn push_node(&mut self, id: AccessibilityId, node: Node) {
        self.nodes.push((id, node))
    }
    pub fn add_node(
        &mut self,
        dioxus_node: &DioxusNode,
        layout_node: &LayoutNode,
        accessibility_id: AccessibilityId,
        node_accessibility: &AccessibilityNodeState,
    ) {
        let mut builder = NodeBuilder::new(Role::Unknown);
        let children = dioxus_node.get_accessibility_children();
        if !children.is_empty() {
            builder.set_children(children);
        }
        if let Some(alt) = &node_accessibility.alt {
            builder.set_value(alt.to_owned());
        } else if let Some(value) = dioxus_node.get_inner_texts() {
            builder.set_value(value);
        }
        if let Some(name) = &node_accessibility.name {
            builder.set_name(name.to_owned());
        }
        if let Some(role) = node_accessibility.role {
            builder.set_role(role);
        }
        let area = layout_node.area.to_f64();
        builder.set_bounds(Rect {
            x0: area.min_x(),
            x1: area.max_x(),
            y0: area.min_y(),
            y1: area.max_y(),
        });
        if node_accessibility.focusable {
            builder.add_action(Action::Focus);
        } else {
            builder.add_action(Action::Default);
            builder.set_default_action_verb(DefaultActionVerb::Focus);
        }
        let node = builder.build();
        self.push_node(accessibility_id, node);
    }
    pub fn set_focus_with_update(&mut self, new_focus_id: AccessibilityId) -> Option<TreeUpdate> {
        self.focused_id = new_focus_id;
        let node_focused_exists = self.nodes.iter().any(|node| node.0 == new_focus_id);
        if node_focused_exists {
            Some(TreeUpdate {
                nodes: Vec::new(),
                tree: None,
                focus: self.focused_id,
            })
        } else {
            None
        }
    }
    pub fn build_root(&mut self, root_name: &str) -> Node {
        let mut builder = NodeBuilder::new(Role::Window);
        builder.set_name(root_name.to_string());
        builder.set_children(
            self.nodes
                .iter()
                .map(|(id, _)| *id)
                .collect::<Vec<AccessibilityId>>(),
        );
        builder.build()
    }
    pub fn process(&mut self, root_id: AccessibilityId, root_name: &str) -> TreeUpdate {
        let root = self.build_root(root_name);
        let mut nodes = vec![(root_id, root)];
        nodes.extend(self.nodes.clone());
        nodes.reverse();
        let focus = self
            .nodes
            .iter()
            .find_map(|node| {
                if node.0 == self.focused_id {
                    Some(node.0)
                } else {
                    None
                }
            })
            .unwrap_or(ACCESSIBILITY_ROOT_ID);
        TreeUpdate {
            nodes,
            tree: Some(Tree::new(root_id)),
            focus,
        }
    }
    pub fn set_focus_on_next_node(&mut self, direction: AccessibilityFocusDirection) -> TreeUpdate {
        let node_index = self
            .nodes
            .iter()
            .enumerate()
            .find(|(_, node)| node.0 == self.focused_id)
            .map(|(i, _)| i);
        let target_node = if direction == AccessibilityFocusDirection::Forward {
            if let Some(node_index) = node_index {
                if node_index == self.nodes.len() - 1 {
                    self.nodes.first()
                } else {
                    self.nodes.get(node_index + 1)
                }
            } else {
                self.nodes.first()
            }
        } else {
            if let Some(node_index) = node_index {
                if node_index == 0 {
                    self.nodes.last()
                } else {
                    self.nodes.get(node_index - 1)
                }
            } else {
                self.nodes.last()
            }
        };
        self.focused_id = target_node
            .map(|(id, _)| *id)
            .unwrap_or(ACCESSIBILITY_ROOT_ID);
        TreeUpdate {
            nodes: Vec::new(),
            tree: None,
            focus: self.focused_id,
        }
    }
}