///|
/// Link found in the page
priv struct Link {
  href : String
  text : String
  source_id : String
}

///|
priv struct ClickDispatchResult {
  handled : Bool
  allow_default : Bool
}

///|
priv struct NavigationRequest {
  url : String
  http_method : String
  content_type : String
  body : String
}

///|
priv enum SubmitDispatchResult {
  NotSubmittable
  Prevented
  Handled
  Navigate(NavigationRequest)
}

///|
priv enum FocusedKeyDispatchResult {
  NotHandled
  Handled
  Activate
  Submit
}

///|
priv struct HitTarget {
  source_id : String
  area : Double
}

///|
priv struct KittyPlacement {
  image_id : Int
  placement_id : Int
  col : Int
  row : Int
  width : Int
  height : Int
}

///|
priv struct KittyRenderSpec {
  region : @tui.ImageRegion
  cached_data : String
  placement : KittyPlacement
}

///|
/// Browser output mode
pub(all) enum OutputMode {
  Sixel
  Kitty
  Text
  Json
  Aom
  Arc90
  ExtractMain
  Grounding
}

///|
/// Browser state
struct Browser {
  /// Current URL
  mut current_url : String
  /// Last navigation request metadata
  mut last_navigation_request : NavigationRequest?
  /// Request sandbox policy
  mut request_sandbox : @http.RequestSandbox
  /// Viewport width in pixels
  viewport_width : Int
  /// Viewport height in pixels
  viewport_height : Int
  /// Current HTML content
  mut html_content : String
  /// Last raw HTML source before render-time normalization
  mut source_html : String
  /// DOM snapshot HTML that preserves serializable shadow roots
  mut dom_snapshot_html : String
  /// Serialized-DOM revision the layout bridge was last injected for. While the
  /// DOM is unchanged (idle / no-mutation run-loop iterations) the injected
  /// layout is still valid, so re-injection skips recomputing the same layout.
  mut bridge_injected_html : String
  /// Parsed document (cached to avoid re-parsing)
  mut parsed_doc : @html.Document?
  /// Whether current render HTML may contain declarative shadow DOM templates
  mut html_has_declarative_shadow_dom : Bool
  /// External CSS content
  mut external_css : Array[String]
  /// Prepared external CSS content
  mut prepared_external_css : @renderer.PreparedExternalCss?
  /// Prepared document/style state for the current HTML/CSS/render context
  mut prepared_render_document : @renderer.PreparedRenderDocument?
  /// Color-scheme flag used by prepared_render_document
  mut prepared_render_document_dark : Bool
  /// Cached render node for graphics output modes
  mut graphics_render_node : @layout.Node?
  /// Cached layout for graphics output modes
  mut graphics_layout : @layout_types.Layout?
  /// Color-scheme flag used by graphics render cache
  mut graphics_render_dark : Bool
  /// Links found in current page
  mut links : Array[Link]
  /// Currently focused link index (legacy, kept for compatibility)
  mut focused_link : Int
  /// Scroll position (Y offset)
  mut scroll_y : Int
  /// Content height (for scroll limit)
  mut content_height : Int
  /// Link regions for click hit testing (updated on each render)
  mut link_regions : Array[@tui.LinkRegion]
  /// Paint-order hit regions for generic click hit testing
  mut hit_regions : Array[@tui.HitRegion]
  /// Hit-a-hint mode state
  mut hint_mode : Bool
  /// Current hints (generated when entering hint mode)
  mut hints : Array[@tui.HintData]
  /// Hint input buffer (for multi-char hints)
  mut hint_input : String
  /// Back history (stack of URLs for going back)
  back_history : Array[String]
  /// Forward history (stack of URLs for going forward)
  forward_history : Array[String]
  /// Focus manager for Tab/Shift+Tab navigation
  mut focus_manager : @aom.FocusManager?
  /// Current accessibility tree
  mut a11y_tree : @aom.AccessibilityTree?
  /// Selection mode (raw mode disabled for text copy)
  mut selection_mode : Bool
  /// Dark mode for display
  mut dark_mode : Bool
  /// Cached render node (for TUI text mode)
  mut render_node : @layout.Node?
  /// Cached layout tree (for incremental layout)
  mut layout_tree : @layout.LayoutTree?
  /// Persistent stable-uid registry: keeps render-node uids stable across the
  /// dynamic path's rebuilds so incremental reflow can match (see
  /// docs/incremental-reflow-design.md).
  uid_registry : @node.UidRegistry
  /// Prior dynamic LayoutTree used as the incremental-reflow reconcile baseline.
  mut incremental_prev_tree : @layout.LayoutTree?
  /// Prior dynamic render-node tree, kept so the next reconcile can diff each
  /// node's layout inputs (computed style via `Style::layout_eq` / text / src /
  /// measure) by uid to produce a tight dirty seed instead of dirtying every node.
  mut incremental_prev_node : @node.Node?
  /// Default on: reconcile the dynamic layout tree against the prior one instead
  /// of a full from-scratch rebuild (equivalence validated on js + native V8 —
  /// see docs/incremental-reflow-design.md). `set_incremental_reflow(false)`
  /// opts back into a full rebuild every render.
  mut enable_incremental_reflow : Bool
  /// Incremental (scoped) cascade — Phase 1, **off by default**. When on, a
  /// reflow whose stylesheet is reuse-safe reuses prior computed styles for
  /// unchanged subtrees instead of re-cascading. See
  /// docs/incremental-cascade-design.md. The prior maps are keyed by the
  /// renderer's `owner_id`; `cascade_reuse_prev_css_sig` invalidates them on a
  /// stylesheet edit.
  mut enable_cascade_reuse : Bool
  mut cascade_reuse_prior_styles : Map[String, @style.Style]
  mut cascade_reuse_prior_sigs : Map[String, String]
  mut cascade_reuse_prev_css_sig : String?
  mut cascade_reuse_last_count : Int
  /// Previous TUI buffer for diff rendering
  mut prev_buffer : @tui.CharBuffer?
  /// Last render key (for diff invalidation)
  mut last_render_key : String
  /// Last color scheme used to build render node
  mut last_color_scheme_dark : Bool
  /// DOM tree for JS execution
  mut dom_tree : @dom.DomTree?
  /// Task scheduler
  scheduler : @scheduler.Scheduler
  /// Script executor for JS execution
  mut script_executor : @js.ScriptExecutor?
  /// Optional external JS runtime (e.g. V8 for native target)
  mut js_runtime_override : &@js.JsRuntime?
  /// Whether JavaScript execution is enabled (requires --enable-js flag)
  mut enable_js : Bool
  /// Element scroll states (keyed by element id)
  /// For elements with overflow: auto/scroll that have scrollable content
  element_scroll_states : Map[String, @dom.ScrollState]
  /// Currently focused scrollable element id (for keyboard scrolling)
  focused_scrollable : Ref[String?]
  /// Currently hovered source element id
  mut hovered_source_id : String?
  /// Source currently pressed by pointer
  mut pressed_source_id : String?
  /// Text-control selection anchor source id during pointer drag
  mut text_selection_anchor_source_id : String?
  /// Text-control selection anchor column during pointer drag
  mut text_selection_anchor_col : Int
  /// Text-control selection anchor row during pointer drag
  mut text_selection_anchor_row : Int
  /// Source currently in drag session
  mut dragging_source_id : String?
  /// Current drag-over target
  mut drag_over_source_id : String?
  /// Current drop-allowed target (set by canceled dragover)
  mut drop_allowed_source_id : String?
  /// No-color mode (disable ANSI color codes)
  mut no_color : Bool
  /// Terminal columns (for pixel-to-column scaling)
  terminal_cols : Int
  /// Image regions from last render (for kitty/sixel overlay)
  mut image_regions : Array[@tui.ImageRegion]
  /// Cached rendered image data (src URL -> kitty graphics commands)
  image_cache : @terminal_image_cache.ImageCache
  /// Uploaded kitty image ids that can be re-placed without re-sending payloads
  kitty_uploaded_image_ids : Map[Int, Bool]
  /// Visible kitty placements from the previous text render
  mut kitty_visible_placements : Map[String, KittyPlacement]
  /// Per-session profile bundling user-agent override, cookie jar,
  /// HTTP cache, and auth state.
  profile : @http_profile.Profile
}

///|
/// Create a new browser instance
pub fn Browser::new(
  width : Int,
  height : Int,
  terminal_cols? : Int = 80,
) -> Browser {
  {
    current_url: "",
    last_navigation_request: None,
    request_sandbox: @http.RequestSandbox::Open,
    viewport_width: width,
    viewport_height: height,
    html_content: "",
    source_html: "",
    dom_snapshot_html: "",
    bridge_injected_html: "",
    parsed_doc: None,
    html_has_declarative_shadow_dom: false,
    external_css: [],
    prepared_external_css: None,
    prepared_render_document: None,
    prepared_render_document_dark: false,
    graphics_render_node: None,
    graphics_layout: None,
    graphics_render_dark: false,
    links: [],
    focused_link: 0,
    scroll_y: 0,
    content_height: 0,
    link_regions: [],
    hit_regions: [],
    hint_mode: false,
    hints: [],
    hint_input: "",
    back_history: [],
    forward_history: [],
    focus_manager: None,
    a11y_tree: None,
    selection_mode: false,
    dark_mode: false,
    render_node: None,
    layout_tree: None,
    uid_registry: @node.UidRegistry::new(),
    incremental_prev_tree: None,
    incremental_prev_node: None,
    enable_incremental_reflow: true,
    enable_cascade_reuse: false,
    cascade_reuse_prior_styles: {},
    cascade_reuse_prior_sigs: {},
    cascade_reuse_prev_css_sig: None,
    cascade_reuse_last_count: 0,
    prev_buffer: None,
    last_render_key: "",
    last_color_scheme_dark: false,
    dom_tree: None,
    scheduler: @scheduler.Scheduler::new(),
    script_executor: None,
    js_runtime_override: None,
    enable_js: false, // Disabled by default for security
    element_scroll_states: {},
    focused_scrollable: Ref(None),
    hovered_source_id: None,
    pressed_source_id: None,
    text_selection_anchor_source_id: None,
    text_selection_anchor_col: 0,
    text_selection_anchor_row: 0,
    dragging_source_id: None,
    drag_over_source_id: None,
    drop_allowed_source_id: None,
    no_color: false,
    terminal_cols,
    image_regions: [],
    image_cache: @terminal_image_cache.ImageCache::new(),
    kitty_uploaded_image_ids: {},
    kitty_visible_placements: {},
    profile: @http_profile.Profile::default(),
  }
}

///|
/// Backward-compatible accessor for the per-session cookie jar.
/// Prefer this method over reaching into Profile internals.
pub fn Browser::cookie_jar(self : Browser) -> @http_cookie_jar.CookieJar {
  self.profile.cookie_jar()
}

///|
/// Backward-compatible accessor for the per-session HTTP cache.
/// Prefer this method over reaching into Profile internals.
pub fn Browser::http_cache(self : Browser) -> @http_cache.MemoryCacheBackend {
  self.profile.http_cache()
}