//! el-platform — Universal rendering backends for el-ui.
//!
//! The same component code produces native output for every target platform.
//! No bridge. No virtual DOM. Direct platform calls.
//!
//! The target is chosen in `el.toml`:
//!
//! ```toml
//! [platform]
//! target = "web"   # web | server | ios | android | macos | linux | windows
//! ssr = true
//! ```
//!
//! All platforms implement the `PlatformBackend` trait. A future agent fills in
//! the native API calls for iOS/Android/macOS/Linux/Windows — the architecture
//! is correct and complete now.

pub mod backends;
pub mod config;
pub mod node;

pub use backends::{
    android::AndroidBackend,
    ios::IosBackend,
    linux::LinuxBackend,
    macos::MacosBackend,
    server::ServerBackend,
    web::WebBackend,
    windows::WindowsBackend,
};
pub use config::{PlatformConfig, PlatformTarget};
pub use node::{Attribute, EventHandler, PlatformNode, PlatformNodeKind};

use thiserror::Error;

#[derive(Debug, Error)]
pub enum PlatformError {
    #[error("render error: {0}")]
    Render(String),
    #[error("mount error: {0}")]
    Mount(String),
    #[error("unsupported operation on target {target}: {op}")]
    Unsupported { target: String, op: String },
    #[error("event binding error: {0}")]
    EventBinding(String),
}

pub type PlatformResult<T> = Result<T, PlatformError>;

/// The core trait every platform backend must implement.
///
/// All rendering paths go through this interface. Component code is identical
/// across targets — only the backend chosen by `el.toml` differs.
pub trait PlatformBackend: Send + Sync {
    /// The platform name (e.g. "web", "server", "ios").
    fn name(&self) -> &'static str;

    /// Create a new element node on this platform.
    fn create_element(&self, tag: &str) -> PlatformResult<PlatformNode>;

    /// Create a text node on this platform.
    fn create_text(&self, content: &str) -> PlatformResult<PlatformNode>;

    /// Set an attribute on a node.
    fn set_attribute(&self, node: &mut PlatformNode, name: &str, value: &str)
        -> PlatformResult<()>;

    /// Remove an attribute from a node.
    fn remove_attribute(&self, node: &mut PlatformNode, name: &str) -> PlatformResult<()>;

    /// Append a child node to a parent.
    fn append_child(&self, parent: &mut PlatformNode, child: PlatformNode)
        -> PlatformResult<()>;

    /// Remove a child node from a parent.
    fn remove_child(&self, parent: &mut PlatformNode, child_index: usize) -> PlatformResult<()>;

    /// Replace a child node at the given index.
    fn replace_child(
        &self,
        parent: &mut PlatformNode,
        index: usize,
        new_child: PlatformNode,
    ) -> PlatformResult<()>;

    /// Bind an event handler to a node.
    fn bind_event(
        &self,
        node: &mut PlatformNode,
        event: &str,
        handler: EventHandler,
    ) -> PlatformResult<()>;

    /// Render a node tree to its platform representation.
    /// For `server`, this returns an HTML string.
    /// For `web`, this patches the live DOM.
    /// For native targets, this calls the appropriate native APIs.
    fn render_to_string(&self, node: &PlatformNode) -> PlatformResult<String>;

    /// Mount a node tree into the platform's root container.
    /// `container_id` is a platform-specific identifier (CSS selector for web,
    /// view controller ID for iOS, activity ID for Android, etc.).
    fn mount(&self, root: PlatformNode, container_id: &str) -> PlatformResult<()>;

    /// Patch an existing mounted tree with a new tree.
    /// The backend performs the minimal update needed.
    fn patch(&self, old: &PlatformNode, new: &PlatformNode) -> PlatformResult<()>;

    /// Whether this backend supports SSR (rendering to HTML string on the server).
    fn supports_ssr(&self) -> bool {
        false
    }
}

/// Select the backend for a given platform target.
pub fn backend_for(target: &PlatformTarget) -> Box<dyn PlatformBackend> {
    match target {
        PlatformTarget::Web => Box::new(WebBackend::new()),
        PlatformTarget::Server => Box::new(ServerBackend::new()),
        PlatformTarget::Ios => Box::new(IosBackend::new()),
        PlatformTarget::Android => Box::new(AndroidBackend::new()),
        PlatformTarget::Macos => Box::new(MacosBackend::new()),
        PlatformTarget::Linux => Box::new(LinuxBackend::new()),
        PlatformTarget::Windows => Box::new(WindowsBackend::new()),
    }
}
