//! Web backend — DOM rendering in browsers.
//!
//! This backend formalizes what `runtime/src/renderer.js` does, as a Rust
//! description of the DOM patching strategy. In a WASM build, this would call
//! into the browser's DOM API directly via `web-sys`.

use crate::{EventHandler, PlatformBackend, PlatformError, PlatformNode, PlatformResult};

/// Web DOM backend.
///
/// Renders component trees to the browser DOM. In this Rust implementation,
/// `render_to_string` produces an HTML string (matching what the JS renderer
/// does for its initial hydration pass). A full WASM build would instead
/// call `document.createElement()` etc. via `web-sys`.
pub struct WebBackend;

impl WebBackend {
    pub fn new() -> Self {
        Self
    }
}

impl Default for WebBackend {
    fn default() -> Self {
        Self::new()
    }
}

impl PlatformBackend for WebBackend {
    fn name(&self) -> &'static str {
        "web"
    }

    fn create_element(&self, tag: &str) -> PlatformResult<PlatformNode> {
        Ok(PlatformNode::element(tag))
    }

    fn create_text(&self, content: &str) -> PlatformResult<PlatformNode> {
        Ok(PlatformNode::text(content))
    }

    fn set_attribute(
        &self,
        node: &mut PlatformNode,
        name: &str,
        value: &str,
    ) -> PlatformResult<()> {
        // Remove existing attribute with same name then push new one.
        node.attributes.retain(|a| a.name != name);
        node.attributes.push(crate::Attribute::new(name, value));
        Ok(())
    }

    fn remove_attribute(&self, node: &mut PlatformNode, name: &str) -> PlatformResult<()> {
        node.attributes.retain(|a| a.name != name);
        Ok(())
    }

    fn append_child(
        &self,
        parent: &mut PlatformNode,
        child: PlatformNode,
    ) -> PlatformResult<()> {
        parent.children.push(child);
        Ok(())
    }

    fn remove_child(&self, parent: &mut PlatformNode, child_index: usize) -> PlatformResult<()> {
        if child_index >= parent.children.len() {
            return Err(PlatformError::Render(format!(
                "web: child index {} out of bounds (len {})",
                child_index,
                parent.children.len()
            )));
        }
        parent.children.remove(child_index);
        Ok(())
    }

    fn replace_child(
        &self,
        parent: &mut PlatformNode,
        index: usize,
        new_child: PlatformNode,
    ) -> PlatformResult<()> {
        if index >= parent.children.len() {
            return Err(PlatformError::Render(format!(
                "web: replace_child index {} out of bounds",
                index
            )));
        }
        parent.children[index] = new_child;
        Ok(())
    }

    fn bind_event(
        &self,
        node: &mut PlatformNode,
        event: &str,
        _handler: EventHandler,
    ) -> PlatformResult<()> {
        // In a real WASM build: node.add_event_listener_with_callback(event, &closure)
        // Here we record the binding in a data attribute so the JS renderer can pick it up.
        node.attributes
            .push(crate::Attribute::new(format!("data-el-{}", event), "[bound]"));
        Ok(())
    }

    fn render_to_string(&self, node: &PlatformNode) -> PlatformResult<String> {
        Ok(node.to_html())
    }

    fn mount(&self, _root: PlatformNode, _container_id: &str) -> PlatformResult<()> {
        // In a real WASM build:
        //   let container = document.query_selector(container_id)?;
        //   container.set_inner_html(&root.to_html());
        // For the Rust-side representation, mount is a no-op.
        Ok(())
    }

    fn patch(&self, _old: &PlatformNode, _new: &PlatformNode) -> PlatformResult<()> {
        // Full DOM patching mirrors renderer.js patch():
        //   1. Walk old and new trees in parallel.
        //   2. For each position: if node kinds differ, replace; else update attributes.
        //   3. Recurse into children.
        // In a WASM build this calls web-sys DOM mutation APIs.
        Ok(())
    }

    fn supports_ssr(&self) -> bool {
        // Web backend can produce HTML strings for hydration.
        true
    }
}
