///|
/// 属性値
pub enum AttrValue {
  Str(String)
  Bool(Bool)
  Int(Int)
  None
} derive(Show, Eq)

///|
pub fn AttrValue::to_string(self : AttrValue) -> String {
  match self {
    Str(s) => s
    Bool(true) => "true"
    Bool(false) => "false"
    Int(i) => i.to_string()
    None => ""
  }
}

///|
/// 文字列からAttrValueを作成
pub fn attr_value_str(s : String) -> AttrValue {
  AttrValue::Str(s)
}

///|
/// 文字列からAttrsを作成
pub fn attr_str(key : String, value : String) -> Attrs {
  Attrs::new().add(key, attr_value_str(value))
}

///|
/// 属性セット
pub struct Attrs {
  attrs : Array[(String, AttrValue)]
} derive(Show)

///|
pub fn Attrs::new() -> Attrs {
  { attrs: [] }
}

///|
pub fn Attrs::add(self : Attrs, key : String, value : AttrValue) -> Attrs {
  { attrs: [(key, value)] + self.attrs }
}

///|
/// 2つの Attrs をマージする(prefer の属性が優先)
pub fn Attrs::merge(prefer : Attrs, fallback : Attrs) -> Attrs {
  // prefer のキーを追跡
  let prefer_keys = @hashmap.new()
  for i = 0; i < prefer.attrs.length(); i = i + 1 {
    let (key, _) = prefer.attrs[i]
    prefer_keys.set(key, "marker")
  }

  // fallback の重複しない属性を収集
  let mut unique_fallback = []
  for i = 0; i < fallback.attrs.length(); i = i + 1 {
    let (key, value) = fallback.attrs[i]
    match prefer_keys.get(key) {
      Some(_) => () // prefer に存在する場合はスキップ
      None => unique_fallback = unique_fallback + [(key, value)]
    }
  }

  // prefer + unique_fallback(順序を維持)
  { attrs: prefer.attrs + unique_fallback }
}

///|
pub fn Attrs::class(self : Attrs, name : String) -> Attrs {
  self.add("class", Str(name))
}

///|
pub fn Attrs::id(self : Attrs, id : String) -> Attrs {
  self.add("id", Str(id))
}

///|
pub fn Attrs::href(self : Attrs, url : String) -> Attrs {
  self.add("href", Str(url))
}

///|
pub fn Attrs::attr_type(self : Attrs, t : String) -> Attrs {
  self.add("type", Str(t))
}

///|
pub fn Attrs::name(self : Attrs, n : String) -> Attrs {
  self.add("name", Str(n))
}

///|
/// Htmx属性: hx-get
pub fn Attrs::hx_get(self : Attrs, url : String) -> Attrs {
  self.add("hx-get", Str(url))
}

///|
/// Htmx属性: hx-post
pub fn Attrs::hx_post(self : Attrs, url : String) -> Attrs {
  self.add("hx-post", Str(url))
}

///|
/// Htmx属性: hx-put
pub fn Attrs::hx_put(self : Attrs, url : String) -> Attrs {
  self.add("hx-put", Str(url))
}

///|
/// Htmx属性: hx-delete
pub fn Attrs::hx_delete(self : Attrs, url : String) -> Attrs {
  self.add("hx-delete", Str(url))
}

///|
/// Htmx属性: hx-patch
pub fn Attrs::hx_patch(self : Attrs, url : String) -> Attrs {
  self.add("hx-patch", Str(url))
}

///|
/// Htmx属性: hx-swap
pub fn Attrs::hx_swap(self : Attrs, swap : String) -> Attrs {
  self.add("hx-swap", Str(swap))
}

///|
/// Htmx属性: hx-target
pub fn Attrs::hx_target(self : Attrs, selector : String) -> Attrs {
  self.add("hx-target", Str(selector))
}

///|
/// Htmx属性: hx-trigger
pub fn Attrs::hx_trigger(self : Attrs, event : String) -> Attrs {
  self.add("hx-trigger", Str(event))
}

///|
pub fn Attrs::value(self : Attrs, v : String) -> Attrs {
  self.add("value", Str(v))
}

///|
pub fn Attrs::placeholder(self : Attrs, p : String) -> Attrs {
  self.add("placeholder", Str(p))
}

///|
pub fn Attrs::data(self : Attrs, key : String, value : String) -> Attrs {
  self.add("data-" + key, Str(value))
}

///|
/// Htmx属性: hx-swap (型安全版 - Swap enumを受け取る)
pub fn Attrs::hx_swap_safe(self : Attrs, swap : Swap) -> Attrs {
  self.hx_swap(swap.to_string())
}

///|
/// Htmx属性: hx-trigger (型安全版 - Trigger enumを受け取る)
pub fn Attrs::hx_trigger_safe(self : Attrs, trigger : Trigger) -> Attrs {
  self.hx_trigger(trigger.to_string())
}

///|
/// HDA Swap戦略
pub enum Swap {
  InnerHTML
  OuterHTML
  BeforeBegin
  AfterBegin
  BeforeEnd
  AfterEnd
  Delete
  None
} derive(Show, Eq)

///|
pub fn Swap::to_string(self : Swap) -> String {
  match self {
    InnerHTML => "innerHTML"
    OuterHTML => "outerHTML"
    BeforeBegin => "beforebegin"
    AfterBegin => "afterbegin"
    BeforeEnd => "beforeend"
    AfterEnd => "afterend"
    Delete => "delete"
    None => "none"
  }
}

///|
pub fn Swap::from_string(s : String) -> Swap {
  match s {
    "innerHTML" => InnerHTML
    "outerHTML" => OuterHTML
    "beforebegin" => BeforeBegin
    "afterbegin" => AfterBegin
    "beforeend" => BeforeEnd
    "afterend" => AfterEnd
    "delete" => Delete
    "none" => None
    _ => None // 不明な値はデフォルトで None
  }
}

///|
/// トリガー修飾子
pub enum TriggerMod {
  Once
  Changed
  Delay(Int)
  Throttle(Int)
  From(String)
  Target(String)
  Filter(String)
  Consumed
  Queue(String)
} derive(Show, Eq)

///|
pub fn TriggerMod::to_string(self : TriggerMod) -> String {
  match self {
    Once => "once"
    Changed => "changed"
    Delay(ms) => "delay:" + ms.to_string() + "ms"
    Throttle(ms) => "throttle:" + ms.to_string() + "ms"
    From(sel) => "from:" + sel
    Target(sel) => "target:" + sel
    Filter(expr) => "filter:" + expr
    Consumed => "consumed"
    Queue(name) => "queue:" + name
  }
}

///|
/// トリガー
pub enum Trigger {
  Event(String)
  EventWithMods(String, Array[TriggerMod])
  Multiple(Array[Trigger])
} derive(Show)

///|
pub fn Trigger::to_string(self : Trigger) -> String {
  match self {
    Event(name) => name
    EventWithMods(name, mods) => {
      let mod_str = mods.map(TriggerMod::to_string).join(" ")
      if mod_str.length() > 0 {
        name + " " + mod_str
      } else {
        name
      }
    }
    Multiple(triggers) => triggers.map(Trigger::to_string).join(", ")
  }
}

///|
/// HTML要素
pub enum Html {
  Element(String, Attrs, Array[Html], Bool)
  Text(String)
  Fragment(Array[Html])
  Raw(String)
  When(Bool, Html, Html?)
} derive(Show)

///|
/// HTMLエスケープ(文字単位処理・UTF-8対応)
fn escape_html(s : String) -> String {
  let mut result : Array[String] = []
  let len = s.length()
  for i = 0; i < len; i = i + 1 {
    let uint = s[i]
    let code = uint.to_int()
    match code {
      38 => result = result + ["&"] // &
      60 => result = result + ["<"] // <
      62 => result = result + [">"] // >
      34 => result = result + ["""] // "
      39 => result = result + ["'"] // '
      _ =>
        // UInt16(Char)をStringに変換
        match uint.to_char() {
          Some(ch) => {
            let char_str = String::from_array([ch])
            result = result + [char_str]
          }
          None => result = result + [""]
        }
    }
  }
  result.join("")
}

///|
/// 属性値エスケープ(文字単位処理・UTF-8対応)
fn escape_attr(s : String) -> String {
  let mut result : Array[String] = []
  let len = s.length()
  for i = 0; i < len; i = i + 1 {
    let uint = s[i]
    let code = uint.to_int()
    match code {
      38 => result = result + ["&"] // &
      34 => result = result + ["""] // "
      60 => result = result + ["<"] // <
      62 => result = result + [">"] // >
      _ =>
        // UInt16(Char)をStringに変換
        match uint.to_char() {
          Some(ch) => {
            let char_str = String::from_array([ch])
            result = result + [char_str]
          }
          None => result = result + [""]
        }
    }
  }
  result.join("")
}

///|
/// 属性を文字列に変換
fn attrs_to_string(attrs : Attrs) -> String {
  let mut result = ""
  let attr_array = attrs.attrs
  for i = 0; i < attr_array.length(); i = i + 1 {
    let (key, value) = attr_array[i]
    let value_str = match value {
      None => " " + key
      Str(s) => " " + key + "=\"" + escape_attr(s) + "\""
      Bool(true) => " " + key + "=\"true\""
      Bool(false) => " " + key + "=\"false\""
      Int(i) => " " + key + "=\"" + i.to_string() + "\""
    }
    result = result + value_str
  }
  result
}

///|
/// 子要素を文字列に変換
fn children_to_string(children : Array[Html]) -> String {
  let mut result = ""
  for i = 0; i < children.length(); i = i + 1 {
    result = result + Html::render(children[i])
  }
  result
}

///|
/// HTMLを文字列にレンダリング
pub fn Html::render(self : Html) -> String {
  match self {
    Element(tag, attrs, children, is_void) => {
      let attr_str = attrs_to_string(attrs)
      let children_str = children_to_string(children)
      if is_void {
        "<" + tag + attr_str + ">"
      } else {
        "<" + tag + attr_str + ">" + children_str + ""
      }
    }
    Text(s) => escape_html(s)
    Fragment(children) => children_to_string(children)
    Raw(s) => s
    When(condition, then, otherwise) =>
      if condition {
        then.render()
      } else {
        match otherwise {
          Some(o) => o.render()
          None => ""
        }
      }
  }
}

///|
/// テキストノード
pub fn text(content : String) -> Html {
  Text(content)
}

///|
/// フラグメント
pub fn fragment(children : Array[Html]) -> Html {
  Fragment(children)
}

///|
/// Raw HTML(エスケープなし)
pub fn raw(content : String) -> Html {
  Raw(content)
}

///|
/// 条件付きレンダリング(elseあり)
pub fn when_else(condition : Bool, then : Html, otherwise : Html) -> Html {
  When(condition, then, Some(otherwise))
}

///|
/// 条件付きレンダリング(elseなし)
pub fn when(condition : Bool, then : Html) -> Html {
  When(condition, then, None)
}

///|
/// 要素ビルダー
pub struct ElementBuilder {
  tag : String
  attrs : Attrs
  is_void : Bool
}

///|
fn ElementBuilder::new(tag : String) -> ElementBuilder {
  { tag, attrs: Attrs::new(), is_void: false }
}

///|
fn ElementBuilder::new_void(tag : String) -> ElementBuilder {
  { tag, attrs: Attrs::new(), is_void: true }
}

///|
pub fn ElementBuilder::attrs(
  self : ElementBuilder,
  attrs : Attrs,
) -> ElementBuilder {
  { ..self, attrs, }
}

///|
pub fn ElementBuilder::attr(
  self : ElementBuilder,
  key : String,
  value : AttrValue,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.add(key, value) }
}

///|
/// 文字列属性を追加(簡易版)
pub fn ElementBuilder::attr_str(
  self : ElementBuilder,
  key : String,
  value : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.add(key, attr_value_str(value)) }
}

///|
pub fn ElementBuilder::class(
  self : ElementBuilder,
  name : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.class(name) }
}

///|
pub fn ElementBuilder::id(self : ElementBuilder, id : String) -> ElementBuilder {
  { ..self, attrs: self.attrs.id(id) }
}

///|
pub fn ElementBuilder::href(
  self : ElementBuilder,
  url : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.href(url) }
}

///|
pub fn ElementBuilder::attr_type(
  self : ElementBuilder,
  t : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.attr_type(t) }
}

///|
pub fn ElementBuilder::name(
  self : ElementBuilder,
  n : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.name(n) }
}

///|
/// Htmx属性: hx-get
pub fn ElementBuilder::hx_get(
  self : ElementBuilder,
  url : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_get(url) }
}

///|
/// Htmx属性: hx-post
pub fn ElementBuilder::hx_post(
  self : ElementBuilder,
  url : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_post(url) }
}

///|
/// Htmx属性: hx-put
pub fn ElementBuilder::hx_put(
  self : ElementBuilder,
  url : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_put(url) }
}

///|
/// Htmx属性: hx-delete
pub fn ElementBuilder::hx_delete(
  self : ElementBuilder,
  url : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_delete(url) }
}

///|
/// Htmx属性: hx-patch
pub fn ElementBuilder::hx_patch(
  self : ElementBuilder,
  url : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_patch(url) }
}

///|
/// Htmx属性: hx-swap
pub fn ElementBuilder::hx_swap(
  self : ElementBuilder,
  swap : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_swap(swap) }
}

///|
/// Htmx属性: hx-target
pub fn ElementBuilder::hx_target(
  self : ElementBuilder,
  selector : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_target(selector) }
}

///|
/// Htmx属性: hx-trigger
pub fn ElementBuilder::hx_trigger(
  self : ElementBuilder,
  event : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_trigger(event) }
}

///|
pub fn ElementBuilder::value(
  self : ElementBuilder,
  v : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.value(v) }
}

///|
pub fn ElementBuilder::placeholder(
  self : ElementBuilder,
  p : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.placeholder(p) }
}

///|
pub fn ElementBuilder::data(
  self : ElementBuilder,
  key : String,
  value : String,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.data(key, value) }
}

///|
/// Htmx属性: hx-swap (型安全版 - Swap enumを受け取る)
pub fn ElementBuilder::hx_swap_safe(
  self : ElementBuilder,
  swap : Swap,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_swap_safe(swap) }
}

///|
/// Htmx属性: hx-trigger (型安全版 - Trigger enumを受け取る)
pub fn ElementBuilder::hx_trigger_safe(
  self : ElementBuilder,
  trigger : Trigger,
) -> ElementBuilder {
  { ..self, attrs: self.attrs.hx_trigger_safe(trigger) }
}

///|
pub fn ElementBuilder::children(
  self : ElementBuilder,
  children : Array[Html],
) -> Html {
  Element(self.tag, self.attrs, children, self.is_void)
}

///|
pub fn ElementBuilder::child(self : ElementBuilder, child : Html) -> Html {
  Element(self.tag, self.attrs, [child], self.is_void)
}

///|
pub fn ElementBuilder::text(self : ElementBuilder, t : String) -> Html {
  Element(self.tag, self.attrs, [Text(t)], self.is_void)
}

///|
pub fn ElementBuilder::empty(self : ElementBuilder) -> Html {
  Element(self.tag, self.attrs, [], self.is_void)
}

///|
/// HTML要素生成関数
pub fn html() -> ElementBuilder {
  ElementBuilder::new("html")
}

///|
pub fn head() -> ElementBuilder {
  ElementBuilder::new("head")
}

///|
pub fn body() -> ElementBuilder {
  ElementBuilder::new("body")
}

///|
pub fn div() -> ElementBuilder {
  ElementBuilder::new("div")
}

///|
pub fn span() -> ElementBuilder {
  ElementBuilder::new("span")
}

///|
pub fn p() -> ElementBuilder {
  ElementBuilder::new("p")
}

///|
pub fn h1() -> ElementBuilder {
  ElementBuilder::new("h1")
}

///|
pub fn h2() -> ElementBuilder {
  ElementBuilder::new("h2")
}

///|
pub fn h3() -> ElementBuilder {
  ElementBuilder::new("h3")
}

///|
pub fn h4() -> ElementBuilder {
  ElementBuilder::new("h4")
}

///|
pub fn h5() -> ElementBuilder {
  ElementBuilder::new("h5")
}

///|
pub fn h6() -> ElementBuilder {
  ElementBuilder::new("h6")
}

///|
pub fn a() -> ElementBuilder {
  ElementBuilder::new("a")
}

///|
pub fn button() -> ElementBuilder {
  ElementBuilder::new("button")
}

///|
pub fn form() -> ElementBuilder {
  ElementBuilder::new("form")
}

///|
pub fn input() -> ElementBuilder {
  ElementBuilder::new_void("input")
}

///|
pub fn textarea() -> ElementBuilder {
  ElementBuilder::new("textarea")
}

///|
pub fn label() -> ElementBuilder {
  ElementBuilder::new("label")
}

///|
pub fn ul() -> ElementBuilder {
  ElementBuilder::new("ul")
}

///|
pub fn ol() -> ElementBuilder {
  ElementBuilder::new("ol")
}

///|
pub fn li() -> ElementBuilder {
  ElementBuilder::new("li")
}

///|
pub fn table() -> ElementBuilder {
  ElementBuilder::new("table")
}

///|
pub fn thead() -> ElementBuilder {
  ElementBuilder::new("thead")
}

///|
pub fn tbody() -> ElementBuilder {
  ElementBuilder::new("tbody")
}

///|
pub fn tr() -> ElementBuilder {
  ElementBuilder::new("tr")
}

///|
pub fn th() -> ElementBuilder {
  ElementBuilder::new("th")
}

///|
pub fn td() -> ElementBuilder {
  ElementBuilder::new("td")
}

///|
pub fn img() -> ElementBuilder {
  ElementBuilder::new_void("img")
}

///|
pub fn br() -> ElementBuilder {
  ElementBuilder::new_void("br")
}

///|
pub fn hr() -> ElementBuilder {
  ElementBuilder::new_void("hr")
}

///|
pub fn meta() -> ElementBuilder {
  ElementBuilder::new_void("meta")
}

///|
pub fn link_tag() -> ElementBuilder {
  ElementBuilder::new_void("link")
}

///|
pub fn script() -> ElementBuilder {
  ElementBuilder::new("script")
}

///|
pub fn style() -> ElementBuilder {
  ElementBuilder::new("style")
}

///|
pub fn title() -> ElementBuilder {
  ElementBuilder::new("title")
}

///|
pub fn nav() -> ElementBuilder {
  ElementBuilder::new("nav")
}

///|
pub fn header() -> ElementBuilder {
  ElementBuilder::new("header")
}

///|
pub fn footer() -> ElementBuilder {
  ElementBuilder::new("footer")
}

///|
pub fn main_el() -> ElementBuilder {
  ElementBuilder::new("main")
}

///|
pub fn section() -> ElementBuilder {
  ElementBuilder::new("section")
}

///|
pub fn article() -> ElementBuilder {
  ElementBuilder::new("article")
}

///|
pub fn aside() -> ElementBuilder {
  ElementBuilder::new("aside")
}

///|
pub fn code() -> ElementBuilder {
  ElementBuilder::new("code")
}

///|
pub fn pre() -> ElementBuilder {
  ElementBuilder::new("pre")
}

///|
pub fn blockquote() -> ElementBuilder {
  ElementBuilder::new("blockquote")
}

///|
pub fn strong() -> ElementBuilder {
  ElementBuilder::new("strong")
}

///|
pub fn em() -> ElementBuilder {
  ElementBuilder::new("em")
}

///|
pub fn del() -> ElementBuilder {
  ElementBuilder::new("del")
}

///|
pub fn small() -> ElementBuilder {
  ElementBuilder::new("small")
}

///|
/// ヘルパー関数
pub fn doctype() -> Html {
  Raw("")
}

///|
pub fn charset(charset : String) -> Html {
  Element("meta", Attrs::new().add("charset", Str(charset)), [], true)
}

///|
pub fn viewport(content : String) -> Html {
  Element(
    "meta",
    Attrs::new().add("name", Str("viewport")).add("content", Str(content)),
    [],
    true,
  )
}

///|
pub fn stylesheet(href : String) -> Html {
  Element(
    "link",
    Attrs::new().add("rel", Str("stylesheet")).add("href", Str(href)),
    [],
    true,
  )
}