// Html Module - DSL for building HTML/DOM structures
//
// This module provides a type-safe way to describe HTML elements that can be
// compiled to efficient JavaScript for browser rendering.
//
// Example usage:
//   let myView = div([class("container")], [
//       h1([id("title")], [text("Hello!")]),
//       button([onClick(Increment)], [text("+")])
//   ])

// Html type represents a DOM structure
// Parameterized by Msg - the type of messages emitted by event handlers
pub type Html<M> =
    | Element(String, List<Attr<M>>, List<Html<M>>)
    | Text(String)
    | Empty

// Attributes that can be applied to elements
pub type Attr<M> =
    | Class(String)
    | Id(String)
    | Style(String, String)
    | Href(String)
    | Src(String)
    | Alt(String)
    | Type(String)
    | Value(String)
    | Placeholder(String)
    | Disabled(Bool)
    | Checked(Bool)
    | Name(String)
    | OnClick(M)
    | OnInput(fn(String): M)
    | OnSubmit(M)
    | OnChange(fn(String): M)
    | OnMouseEnter(M)
    | OnMouseLeave(M)
    | OnFocus(M)
    | OnBlur(M)
    | OnKeyDown(fn(String): M)
    | OnKeyUp(fn(String): M)
    | DataAttr(String, String)

// ============================================================================
// Element builders - Container elements
// ============================================================================

pub fn div<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("div", attrs, children)

pub fn span<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("span", attrs, children)

pub fn section<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("section", attrs, children)

pub fn article<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("article", attrs, children)

pub fn header<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("header", attrs, children)

pub fn footer<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("footer", attrs, children)

pub fn nav<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("nav", attrs, children)

pub fn main<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("main", attrs, children)

pub fn aside<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("aside", attrs, children)

// ============================================================================
// Element builders - Text elements
// ============================================================================

pub fn h1<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("h1", attrs, children)

pub fn h2<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("h2", attrs, children)

pub fn h3<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("h3", attrs, children)

pub fn h4<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("h4", attrs, children)

pub fn h5<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("h5", attrs, children)

pub fn h6<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("h6", attrs, children)

pub fn p<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("p", attrs, children)

pub fn pre<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("pre", attrs, children)

pub fn code<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("code", attrs, children)

pub fn blockquote<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("blockquote", attrs, children)

// ============================================================================
// Element builders - Inline elements
// ============================================================================

pub fn a<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("a", attrs, children)

pub fn strong<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("strong", attrs, children)

pub fn em<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("em", attrs, children)

pub fn small<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("small", attrs, children)

pub fn br<M>(): Html<M> =
    Element("br", [], [])

pub fn hr<M>(): Html<M> =
    Element("hr", [], [])

// ============================================================================
// Element builders - Lists
// ============================================================================

pub fn ul<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("ul", attrs, children)

pub fn ol<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("ol", attrs, children)

pub fn li<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("li", attrs, children)

// ============================================================================
// Element builders - Forms
// ============================================================================

pub fn form<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("form", attrs, children)

pub fn input<M>(attrs: List<Attr<M>>): Html<M> =
    Element("input", attrs, [])

pub fn textarea<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("textarea", attrs, children)

pub fn button<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("button", attrs, children)

pub fn label<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("label", attrs, children)

pub fn select<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("select", attrs, children)

pub fn option<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("option", attrs, children)

// ============================================================================
// Element builders - Media
// ============================================================================

pub fn img<M>(attrs: List<Attr<M>>): Html<M> =
    Element("img", attrs, [])

pub fn video<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("video", attrs, children)

pub fn audio<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("audio", attrs, children)

// ============================================================================
// Element builders - Tables
// ============================================================================

pub fn table<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("table", attrs, children)

pub fn thead<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("thead", attrs, children)

pub fn tbody<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("tbody", attrs, children)

pub fn tr<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("tr", attrs, children)

pub fn th<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("th", attrs, children)

pub fn td<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
    Element("td", attrs, children)

// ============================================================================
// Text and empty nodes
// ============================================================================

pub fn text<M>(content: String): Html<M> =
    Text(content)

pub fn empty<M>(): Html<M> =
    Empty

// ============================================================================
// Attribute helpers
// ============================================================================

pub fn class<M>(name: String): Attr<M> =
    Class(name)

pub fn id<M>(name: String): Attr<M> =
    Id(name)

pub fn style<M>(property: String, value: String): Attr<M> =
    Style(property, value)

pub fn href<M>(url: String): Attr<M> =
    Href(url)

pub fn src<M>(url: String): Attr<M> =
    Src(url)

pub fn alt<M>(description: String): Attr<M> =
    Alt(description)

pub fn inputType<M>(t: String): Attr<M> =
    Type(t)

pub fn value<M>(v: String): Attr<M> =
    Value(v)

pub fn placeholder<M>(p: String): Attr<M> =
    Placeholder(p)

pub fn disabled<M>(d: Bool): Attr<M> =
    Disabled(d)

pub fn checked<M>(c: Bool): Attr<M> =
    Checked(c)

pub fn name<M>(n: String): Attr<M> =
    Name(n)

pub fn onClick<M>(msg: M): Attr<M> =
    OnClick(msg)

pub fn onInput<M>(h: fn(String): M): Attr<M> =
    OnInput(h)

pub fn onSubmit<M>(msg: M): Attr<M> =
    OnSubmit(msg)

pub fn onChange<M>(h: fn(String): M): Attr<M> =
    OnChange(h)

pub fn onMouseEnter<M>(msg: M): Attr<M> =
    OnMouseEnter(msg)

pub fn onMouseLeave<M>(msg: M): Attr<M> =
    OnMouseLeave(msg)

pub fn onFocus<M>(msg: M): Attr<M> =
    OnFocus(msg)

pub fn onBlur<M>(msg: M): Attr<M> =
    OnBlur(msg)

pub fn onKeyDown<M>(h: fn(String): M): Attr<M> =
    OnKeyDown(h)

pub fn onKeyUp<M>(h: fn(String): M): Attr<M> =
    OnKeyUp(h)

pub fn data<M>(name: String, value: String): Attr<M> =
    DataAttr(name, value)

// ============================================================================
// Utility functions
// ============================================================================

// Conditionally include an element
pub fn when<M>(condition: Bool, element: Html<M>): Html<M> =
    if condition then element else Empty

// Conditionally apply attributes
pub fn attrIf<M>(condition: Bool, attr: Attr<M>): List<Attr<M>> =
    if condition then [attr] else []

// ============================================================================
// Static HTML Rendering (for SSG)
// ============================================================================

// Render an attribute to a string
pub fn renderAttr<M>(attr: Attr<M>): String =
    match attr {
        Class(name) => " class=\"" + name + "\"",
        Id(name) => " id=\"" + name + "\"",
        Style(prop, val) => " style=\"" + prop + ": " + val + "\"",
        Href(url) => " href=\"" + url + "\"",
        Src(url) => " src=\"" + url + "\"",
        Alt(desc) => " alt=\"" + desc + "\"",
        Type(t) => " type=\"" + t + "\"",
        Value(v) => " value=\"" + v + "\"",
        Placeholder(p) => " placeholder=\"" + p + "\"",
        Disabled(true) => " disabled",
        Disabled(false) => "",
        Checked(true) => " checked",
        Checked(false) => "",
        Name(n) => " name=\"" + n + "\"",
        DataAttr(name, value) => " data-" + name + "=\"" + value + "\"",
        // Event handlers are ignored in static rendering
        OnClick(_) => "",
        OnInput(_) => "",
        OnSubmit(_) => "",
        OnChange(_) => "",
        OnMouseEnter(_) => "",
        OnMouseLeave(_) => "",
        OnFocus(_) => "",
        OnBlur(_) => "",
        OnKeyDown(_) => "",
        OnKeyUp(_) => ""
    }

// Render attributes list to string
pub fn renderAttrs<M>(attrs: List<Attr<M>>): String =
    List.fold(attrs, "", fn(acc, attr) => acc + renderAttr(attr))

// Self-closing tags
pub fn isSelfClosing(tag: String): Bool =
    tag == "br" || tag == "hr" || tag == "img" || tag == "input" ||
    tag == "meta" || tag == "link" || tag == "area" || tag == "base" ||
    tag == "col" || tag == "embed" || tag == "source" || tag == "track" || tag == "wbr"

// Render Html to string
pub fn render<M>(html: Html<M>): String =
    match html {
        Element(tag, attrs, children) => {
            let attrStr = renderAttrs(attrs)
            if isSelfClosing(tag) then
                "<" + tag + attrStr + " />"
            else {
                let childrenStr = List.fold(children, "", fn(acc, child) => acc + render(child))
                "<" + tag + attrStr + ">" + childrenStr + "</" + tag + ">"
            }
        },
        Text(content) => escapeHtml(content),
        Empty => ""
    }

// Escape HTML special characters
pub fn escapeHtml(s: String): String = {
    // Simple replacement - a full implementation would handle all entities
    let s1 = String.replace(s, "&", "&amp;")
    let s2 = String.replace(s1, "<", "&lt;")
    let s3 = String.replace(s2, ">", "&gt;")
    let s4 = String.replace(s3, "\"", "&quot;")
    s4
}

// Render a full HTML document
pub fn document(title: String, headExtra: List<Html<M>>, bodyContent: List<Html<M>>): String = {
    let headElements = List.concat([
        [Element("meta", [DataAttr("charset", "UTF-8")], [])],
        [Element("meta", [Name("viewport"), Value("width=device-width, initial-scale=1.0")], [])],
        [Element("title", [], [Text(title)])],
        headExtra
    ])
    let doc = Element("html", [DataAttr("lang", "en")], [
        Element("head", [], headElements),
        Element("body", [], bodyContent)
    ])
    "<!DOCTYPE html>\n" + render(doc)
}
