///|
/// Accessibility Object Model (AOM) Types
/// Based on WAI-ARIA 1.2 and HTML-AAM 1.0 specifications

// =============================================================================
// ARIA Roles (WAI-ARIA 1.2)
// =============================================================================

///|
/// ARIA Role categories based on WAI-ARIA specification
pub(all) enum Role {
  // Document structure roles
  Article
  Blockquote
  Caption
  Cell
  Code
  ColumnHeader
  Definition
  Deletion
  Directory
  Document
  Emphasis
  Feed
  Figure
  Generic
  Group
  Heading
  Img
  Insertion
  List
  ListItem
  Mark
  Math
  Meter
  None
  Note
  Paragraph
  Presentation
  Row
  RowGroup
  RowHeader
  Separator
  Strong
  Subscript
  Superscript
  Table
  Term
  Time
  Toolbar
  Tooltip

  // Widget roles
  Button
  Checkbox
  Combobox
  GridCell
  Link
  Listbox
  Menu
  MenuBar
  MenuItem
  MenuItemCheckbox
  MenuItemRadio
  Option
  Progressbar
  Radio
  RadioGroup
  Scrollbar
  SearchBox
  Slider
  SpinButton
  Switch
  Tab
  TabList
  TabPanel
  Textbox
  Tree
  TreeGrid
  TreeItem

  // Landmark roles
  Application
  Banner
  Complementary
  ContentInfo
  Form
  Main
  Navigation
  Region
  Search

  // Live region roles
  Alert
  AlertDialog
  Dialog
  Log
  Marquee
  Status
  Timer

  // Window roles
  Window

  // HTML-specific mappings (not in ARIA but needed for HTML-AAM)
  HtmlAudio
  HtmlVideo
  HtmlCanvas
  HtmlLabel
  HtmlSummary
} derive(Eq, Debug)

///|
pub fn Role::to_string(self : Role) -> String {
  match self {
    Article => "article"
    Blockquote => "blockquote"
    Caption => "caption"
    Cell => "cell"
    Code => "code"
    ColumnHeader => "columnheader"
    Definition => "definition"
    Deletion => "deletion"
    Directory => "directory"
    Document => "document"
    Emphasis => "emphasis"
    Feed => "feed"
    Figure => "figure"
    Generic => "generic"
    Group => "group"
    Heading => "heading"
    Img => "img"
    Insertion => "insertion"
    List => "list"
    ListItem => "listitem"
    Mark => "mark"
    Math => "math"
    Meter => "meter"
    None => "none"
    Note => "note"
    Paragraph => "paragraph"
    Presentation => "presentation"
    Row => "row"
    RowGroup => "rowgroup"
    RowHeader => "rowheader"
    Separator => "separator"
    Strong => "strong"
    Subscript => "subscript"
    Superscript => "superscript"
    Table => "table"
    Term => "term"
    Time => "time"
    Toolbar => "toolbar"
    Tooltip => "tooltip"
    Button => "button"
    Checkbox => "checkbox"
    Combobox => "combobox"
    GridCell => "gridcell"
    Link => "link"
    Listbox => "listbox"
    Menu => "menu"
    MenuBar => "menubar"
    MenuItem => "menuitem"
    MenuItemCheckbox => "menuitemcheckbox"
    MenuItemRadio => "menuitemradio"
    Option => "option"
    Progressbar => "progressbar"
    Radio => "radio"
    RadioGroup => "radiogroup"
    Scrollbar => "scrollbar"
    SearchBox => "searchbox"
    Slider => "slider"
    SpinButton => "spinbutton"
    Switch => "switch"
    Tab => "tab"
    TabList => "tablist"
    TabPanel => "tabpanel"
    Textbox => "textbox"
    Tree => "tree"
    TreeGrid => "treegrid"
    TreeItem => "treeitem"
    Application => "application"
    Banner => "banner"
    Complementary => "complementary"
    ContentInfo => "contentinfo"
    Form => "form"
    Main => "main"
    Navigation => "navigation"
    Region => "region"
    Search => "search"
    Alert => "alert"
    AlertDialog => "alertdialog"
    Dialog => "dialog"
    Log => "log"
    Marquee => "marquee"
    Status => "status"
    Timer => "timer"
    Window => "window"
    HtmlAudio => "html-audio"
    HtmlVideo => "html-video"
    HtmlCanvas => "html-canvas"
    HtmlLabel => "html-label"
    HtmlSummary => "html-summary"
  }
}

///|
pub impl Show for Role with fn output(self, logger) {
  match self {
    Article => logger.write_string("Article")
    Blockquote => logger.write_string("Blockquote")
    Caption => logger.write_string("Caption")
    Cell => logger.write_string("Cell")
    Code => logger.write_string("Code")
    ColumnHeader => logger.write_string("ColumnHeader")
    Definition => logger.write_string("Definition")
    Deletion => logger.write_string("Deletion")
    Directory => logger.write_string("Directory")
    Document => logger.write_string("Document")
    Emphasis => logger.write_string("Emphasis")
    Feed => logger.write_string("Feed")
    Figure => logger.write_string("Figure")
    Generic => logger.write_string("Generic")
    Group => logger.write_string("Group")
    Heading => logger.write_string("Heading")
    Img => logger.write_string("Img")
    Insertion => logger.write_string("Insertion")
    List => logger.write_string("List")
    ListItem => logger.write_string("ListItem")
    Mark => logger.write_string("Mark")
    Math => logger.write_string("Math")
    Meter => logger.write_string("Meter")
    None => logger.write_string("None")
    Note => logger.write_string("Note")
    Paragraph => logger.write_string("Paragraph")
    Presentation => logger.write_string("Presentation")
    Row => logger.write_string("Row")
    RowGroup => logger.write_string("RowGroup")
    RowHeader => logger.write_string("RowHeader")
    Separator => logger.write_string("Separator")
    Strong => logger.write_string("Strong")
    Subscript => logger.write_string("Subscript")
    Superscript => logger.write_string("Superscript")
    Table => logger.write_string("Table")
    Term => logger.write_string("Term")
    Time => logger.write_string("Time")
    Toolbar => logger.write_string("Toolbar")
    Tooltip => logger.write_string("Tooltip")
    Button => logger.write_string("Button")
    Checkbox => logger.write_string("Checkbox")
    Combobox => logger.write_string("Combobox")
    GridCell => logger.write_string("GridCell")
    Link => logger.write_string("Link")
    Listbox => logger.write_string("Listbox")
    Menu => logger.write_string("Menu")
    MenuBar => logger.write_string("MenuBar")
    MenuItem => logger.write_string("MenuItem")
    MenuItemCheckbox => logger.write_string("MenuItemCheckbox")
    MenuItemRadio => logger.write_string("MenuItemRadio")
    Option => logger.write_string("Option")
    Progressbar => logger.write_string("Progressbar")
    Radio => logger.write_string("Radio")
    RadioGroup => logger.write_string("RadioGroup")
    Scrollbar => logger.write_string("Scrollbar")
    SearchBox => logger.write_string("SearchBox")
    Slider => logger.write_string("Slider")
    SpinButton => logger.write_string("SpinButton")
    Switch => logger.write_string("Switch")
    Tab => logger.write_string("Tab")
    TabList => logger.write_string("TabList")
    TabPanel => logger.write_string("TabPanel")
    Textbox => logger.write_string("Textbox")
    Tree => logger.write_string("Tree")
    TreeGrid => logger.write_string("TreeGrid")
    TreeItem => logger.write_string("TreeItem")
    Application => logger.write_string("Application")
    Banner => logger.write_string("Banner")
    Complementary => logger.write_string("Complementary")
    ContentInfo => logger.write_string("ContentInfo")
    Form => logger.write_string("Form")
    Main => logger.write_string("Main")
    Navigation => logger.write_string("Navigation")
    Region => logger.write_string("Region")
    Search => logger.write_string("Search")
    Alert => logger.write_string("Alert")
    AlertDialog => logger.write_string("AlertDialog")
    Dialog => logger.write_string("Dialog")
    Log => logger.write_string("Log")
    Marquee => logger.write_string("Marquee")
    Status => logger.write_string("Status")
    Timer => logger.write_string("Timer")
    Window => logger.write_string("Window")
    HtmlAudio => logger.write_string("HtmlAudio")
    HtmlVideo => logger.write_string("HtmlVideo")
    HtmlCanvas => logger.write_string("HtmlCanvas")
    HtmlLabel => logger.write_string("HtmlLabel")
    HtmlSummary => logger.write_string("HtmlSummary")
  }
}

///|
pub fn Role::from_string(s : String) -> Role? {
  match s {
    "article" => Some(Article)
    "blockquote" => Some(Blockquote)
    "caption" => Some(Caption)
    "cell" => Some(Cell)
    "code" => Some(Code)
    "columnheader" => Some(ColumnHeader)
    "definition" => Some(Definition)
    "deletion" => Some(Deletion)
    "directory" => Some(Directory)
    "document" => Some(Document)
    "emphasis" => Some(Emphasis)
    "feed" => Some(Feed)
    "figure" => Some(Figure)
    "generic" => Some(Generic)
    "group" => Some(Group)
    "heading" => Some(Heading)
    "img" | "image" => Some(Img)
    "insertion" => Some(Insertion)
    "list" => Some(List)
    "listitem" => Some(ListItem)
    "mark" => Some(Mark)
    "math" => Some(Math)
    "meter" => Some(Meter)
    "none" => Some(None)
    "note" => Some(Note)
    "paragraph" => Some(Paragraph)
    "presentation" => Some(Presentation)
    "row" => Some(Row)
    "rowgroup" => Some(RowGroup)
    "rowheader" => Some(RowHeader)
    "separator" => Some(Separator)
    "strong" => Some(Strong)
    "subscript" => Some(Subscript)
    "superscript" => Some(Superscript)
    "table" => Some(Table)
    "term" => Some(Term)
    "time" => Some(Time)
    "toolbar" => Some(Toolbar)
    "tooltip" => Some(Tooltip)
    "button" => Some(Button)
    "checkbox" => Some(Checkbox)
    "combobox" => Some(Combobox)
    "gridcell" => Some(GridCell)
    "link" => Some(Link)
    "listbox" => Some(Listbox)
    "menu" => Some(Menu)
    "menubar" => Some(MenuBar)
    "menuitem" => Some(MenuItem)
    "menuitemcheckbox" => Some(MenuItemCheckbox)
    "menuitemradio" => Some(MenuItemRadio)
    "option" => Some(Option)
    "progressbar" => Some(Progressbar)
    "radio" => Some(Radio)
    "radiogroup" => Some(RadioGroup)
    "scrollbar" => Some(Scrollbar)
    "searchbox" => Some(SearchBox)
    "slider" => Some(Slider)
    "spinbutton" => Some(SpinButton)
    "switch" => Some(Switch)
    "tab" => Some(Tab)
    "tablist" => Some(TabList)
    "tabpanel" => Some(TabPanel)
    "textbox" => Some(Textbox)
    "tree" => Some(Tree)
    "treegrid" => Some(TreeGrid)
    "treeitem" => Some(TreeItem)
    "application" => Some(Application)
    "banner" => Some(Banner)
    "complementary" => Some(Complementary)
    "contentinfo" => Some(ContentInfo)
    "form" => Some(Form)
    "main" => Some(Main)
    "navigation" => Some(Navigation)
    "region" => Some(Region)
    "search" => Some(Search)
    "alert" => Some(Alert)
    "alertdialog" => Some(AlertDialog)
    "dialog" => Some(Dialog)
    "log" => Some(Log)
    "marquee" => Some(Marquee)
    "status" => Some(Status)
    "timer" => Some(Timer)
    "window" => Some(Window)
    _ => Option::None
  }
}

// =============================================================================
// ARIA States and Properties
// =============================================================================

///|
/// ARIA state values
pub(all) enum State {
  // Boolean states
  Busy
  Checked
  Disabled
  Expanded
  Grabbed
  Hidden
  Invalid
  Pressed
  Selected

  // Tristate
  CheckedMixed
  PressedMixed
} derive(Eq, Debug)

///|
pub fn State::to_string(self : State) -> String {
  match self {
    Busy => "busy"
    Checked => "checked"
    Disabled => "disabled"
    Expanded => "expanded"
    Grabbed => "grabbed"
    Hidden => "hidden"
    Invalid => "invalid"
    Pressed => "pressed"
    Selected => "selected"
    CheckedMixed => "mixed"
    PressedMixed => "mixed"
  }
}

///|
pub impl Show for State with fn output(self, logger) {
  match self {
    Busy => logger.write_string("Busy")
    Checked => logger.write_string("Checked")
    Disabled => logger.write_string("Disabled")
    Expanded => logger.write_string("Expanded")
    Grabbed => logger.write_string("Grabbed")
    Hidden => logger.write_string("Hidden")
    Invalid => logger.write_string("Invalid")
    Pressed => logger.write_string("Pressed")
    Selected => logger.write_string("Selected")
    CheckedMixed => logger.write_string("CheckedMixed")
    PressedMixed => logger.write_string("PressedMixed")
  }
}

// =============================================================================
// Accessibility Node
// =============================================================================

///|
/// Bounding rectangle for spatial information
pub(all) struct Bounds {
  x : Double
  y : Double
  width : Double
  height : Double
}

///|
pub fn Bounds::new(
  x : Double,
  y : Double,
  width : Double,
  height : Double,
) -> Bounds {
  { x, y, width, height }
}

///|
pub fn Bounds::default() -> Bounds {
  { x: 0.0, y: 0.0, width: 0.0, height: 0.0 }
}

///|
/// Core accessibility node in the AOM tree
pub(all) struct AccessibilityNode {
  // Identity
  id : String

  // HTML-AAM computed role
  role : Role

  // Accessible name (accname spec)
  name : String?

  // Accessible description
  description : String?

  // Heading level (1-6, only for Heading role)
  level : Int?

  // Current states
  states : Array[State]

  // ARIA properties
  properties : Map[String, String]

  // Spatial information (from layout)
  bounds : Bounds?

  // Tree structure
  children : Array[AccessibilityNode]

  // Focusable / interactive
  focusable : Bool

  // Tabindex value for tab navigation order
  // None = no explicit tabindex (inherent focusability only)
  // Some(-1) = programmatically focusable but not via Tab
  // Some(0) = focusable in document order
  // Some(n) where n > 0 = focusable in priority order
  tabindex : Int?

  // Reference to source element id (for debugging/tooling)
  source_id : String?

  // === AI scraping enhancements ===

  // URL for links (href attribute)
  href : String?

  // Original HTML tag name (for disambiguation when role is generic)
  tag_name : String?

  // CSS selector for element targeting (for AI interaction)
  selector : String?

  // Whether this is visible content (false for style, script, head content)
  visible : Bool

  // Sequential reference ID for AI interaction (e.g., "ref_1", "ref_2")
  // Simple, unique identifier for element targeting
  ref_id : String?

  // === Form input enhancements ===

  // Input type for form elements (text, email, password, checkbox, radio, etc.)
  input_type : String?

  // Placeholder text for form inputs
  placeholder : String?

  // Current value of form inputs
  input_value : String?

  // Whether the form field is required
  required : Bool

  // === AI text aggregation ===

  // Aggregated text content from all children (for paragraphs, etc.)
  text : String?

  // === Select element options ===

  // Options for select/listbox elements
  options : Array[SelectOption]

  // === Label associations ===

  // Ref ID of the element that labels this element
  labelled_by : String?
}

///|
/// Option for select elements
pub(all) struct SelectOption {
  value : String
  label : String
  selected : Bool
}

///|
pub fn AccessibilityNode::new(id : String, role : Role) -> AccessibilityNode {
  {
    id,
    role,
    name: Option::None,
    description: Option::None,
    level: Option::None,
    states: [],
    properties: {},
    bounds: Option::None,
    children: [],
    focusable: false,
    tabindex: Option::None,
    source_id: Option::None,
    href: Option::None,
    tag_name: Option::None,
    selector: Option::None,
    visible: true,
    ref_id: Option::None,
    input_type: Option::None,
    placeholder: Option::None,
    input_value: Option::None,
    required: false,
    text: Option::None,
    options: [],
    labelled_by: Option::None,
  }
}

///|
pub fn AccessibilityNode::with_name(
  self : AccessibilityNode,
  name : String,
) -> AccessibilityNode {
  { ..self, name: Some(name) }
}

///|
pub fn AccessibilityNode::with_bounds(
  self : AccessibilityNode,
  bounds : Bounds,
) -> AccessibilityNode {
  { ..self, bounds: Some(bounds) }
}

///|
pub fn AccessibilityNode::with_level(
  self : AccessibilityNode,
  level : Int,
) -> AccessibilityNode {
  { ..self, level: Some(level) }
}

///|
pub fn AccessibilityNode::add_state(
  self : AccessibilityNode,
  state : State,
) -> AccessibilityNode {
  let new_states = self.states.copy()
  new_states.push(state)
  { ..self, states: new_states }
}

///|
pub fn AccessibilityNode::add_child(
  self : AccessibilityNode,
  child : AccessibilityNode,
) -> AccessibilityNode {
  let new_children = self.children.copy()
  new_children.push(child)
  { ..self, children: new_children }
}

///|
pub fn AccessibilityNode::set_focusable(
  self : AccessibilityNode,
  focusable : Bool,
) -> AccessibilityNode {
  { ..self, focusable, }
}

///|
pub fn AccessibilityNode::with_tabindex(
  self : AccessibilityNode,
  tabindex : Int,
) -> AccessibilityNode {
  { ..self, tabindex: Some(tabindex) }
}

// =============================================================================
// Accessibility Tree
// =============================================================================

///|
/// The complete accessibility tree
pub(all) struct AccessibilityTree {
  root : AccessibilityNode
  // Index for fast lookup by id
  node_map : Map[String, AccessibilityNode]
}

///|
pub fn AccessibilityTree::new(root : AccessibilityNode) -> AccessibilityTree {
  let node_map : Map[String, AccessibilityNode] = {}
  build_node_map(root, node_map)
  { root, node_map }
}

///|
fn build_node_map(
  node : AccessibilityNode,
  map : Map[String, AccessibilityNode],
) -> Unit {
  map[node.id] = node
  for child in node.children {
    build_node_map(child, map)
  }
}

///|
pub fn AccessibilityTree::find_by_id(
  self : AccessibilityTree,
  id : String,
) -> AccessibilityNode? {
  self.node_map.get(id)
}

///|
pub fn AccessibilityTree::find_by_role(
  self : AccessibilityTree,
  role : Role,
) -> Array[AccessibilityNode] {
  let result : Array[AccessibilityNode] = []
  find_by_role_recursive(self.root, role, result)
  result
}

///|
fn find_by_role_recursive(
  node : AccessibilityNode,
  role : Role,
  result : Array[AccessibilityNode],
) -> Unit {
  if node.role == role {
    result.push(node)
  }
  for child in node.children {
    find_by_role_recursive(child, role, result)
  }
}

///|
pub fn AccessibilityTree::find_focusable(
  self : AccessibilityTree,
) -> Array[AccessibilityNode] {
  let result : Array[AccessibilityNode] = []
  find_focusable_recursive(self.root, result)
  result
}

///|
fn find_focusable_recursive(
  node : AccessibilityNode,
  result : Array[AccessibilityNode],
) -> Unit {
  if node.focusable {
    result.push(node)
  }
  for child in node.children {
    find_focusable_recursive(child, result)
  }
}

///|
/// Find node by its source element id
pub fn AccessibilityTree::find_by_source_id(
  self : AccessibilityTree,
  source_id : String,
) -> AccessibilityNode? {
  find_by_source_id_recursive(self.root, source_id)
}

///|
fn find_by_source_id_recursive(
  node : AccessibilityNode,
  source_id : String,
) -> AccessibilityNode? {
  match node.source_id {
    Some(id) if id == source_id => return Some(node)
    _ => ()
  }
  for child in node.children {
    match find_by_source_id_recursive(child, source_id) {
      Some(found) => return Some(found)
      Option::None => ()
    }
  }
  Option::None
}