///|
/// What an inline reference points at. Image and Video can carry a
/// `thumbnail`; every kind falls back to its own glyph when no thumbnail
/// is given (or the state is `Empty`).
pub(all) enum MediaTagKind {
  Image
  Video
  Audio
  Text
  Group
  Timeline
  Element
} derive(Eq)

///|
pub extend MediaTagKind with Eq::{equal, not_equal}

///|
/// Lifecycle of the referenced content: `Candidate` is a half-transparent
/// suggestion, `Uploading` dims the thumbnail under a spinner, `Generating`
/// swaps the visual for a spinner, `Failed` shows the warning disc, and
/// `Empty` forces the placeholder glyph even when a thumbnail exists.
pub(all) enum MediaTagState {
  Ready
  Candidate
  Uploading
  Generating
  Failed
  Empty
} derive(Eq)

///|
pub extend MediaTagState with Eq::{equal, not_equal}

///|
/// `Block` is the filled chip used inside conversation bubbles; `Inline`
/// is the transparent variant that sits inside the composer input.
pub(all) enum MediaTagVariant {
  Block
  Inline
} derive(Eq)

///|
pub extend MediaTagVariant with Eq::{equal, not_equal}

///|
const MediaTagStyle : String = "display:inline-flex;align-items:center;gap:4px;height:24px;margin:0;border:none;border-radius:8px;font-size:14px;line-height:24px;font-weight:400;white-space:nowrap;color:var(--sumi-text-secondary,rgba(255,255,255,0.7));user-select:none;appearance:none;-webkit-appearance:none;outline:none;text-decoration:none"

///|
const MediaTagBlockStyle : String = "padding:0 8px 0 4px;background:var(--sumi-block,rgba(255,255,255,0.08))"

///|
const MediaTagInlineStyle : String = "padding:0 4px;background:transparent"

///|
const MediaTagVisualStyle : String = "display:inline-flex;width:16px;height:16px;flex:none;align-items:center;justify-content:center"

///|
fn media_tag_kind_name(kind : MediaTagKind) -> String {
  match kind {
    Image => "image"
    Video => "video"
    Audio => "audio"
    Text => "text"
    Group => "group"
    Timeline => "timeline"
    Element => "element"
  }
}

///|
fn media_tag_state_name(state : MediaTagState) -> String {
  match state {
    Ready => "ready"
    Candidate => "candidate"
    Uploading => "uploading"
    Generating => "generating"
    Failed => "failed"
    Empty => "empty"
  }
}

///|
fn media_tag_kind_icon(kind : MediaTagKind) -> @html.Html {
  match kind {
    Image => icon_image_fill()
    Video => icon_video()
    Audio => icon_sound()
    Text => icon_text()
    Group => icon_group()
    Timeline => icon_timeline()
    Element => icon_element()
  }
}

///|
/// The 16px square thumbnail, optionally dimmed under a centered spinner
/// while the content uploads.
fn media_tag_thumb(src : String, uploading : Bool) -> @html.Html {
  let children : Array[@html.Html] = [
    @html.img(src~, alt="", style=[
      "position:absolute;inset:0;width:100%;height:100%;object-fit:cover;pointer-events:none",
    ]),
  ]
  if uploading {
    children.push(
      @html.span(
        style=["position:absolute;inset:0;background:rgba(0,0,0,0.3)"],
        ([] : Array[@html.Html]),
      ),
    )
    children.push(
      @html.span(
        style=[
          "position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:inline-flex",
        ],
        [spinner(size=12)],
      ),
    )
  }
  @html.span(
    style=[
      "position:relative;display:inline-block;width:16px;height:16px;flex:none;border-radius:4px;overflow:hidden",
    ],
    attrs=@html.Attrs::build().data_set("slot", "media-tag-thumb"),
    children,
  )
}

///|
fn media_tag_visual(
  kind : MediaTagKind,
  state : MediaTagState,
  thumbnail : String?,
) -> @html.Html {
  match state {
    Failed =>
      @html.span(
        style=[MediaTagVisualStyle, "color:var(--sumi-favorite,#ffa21e)"],
        [icon_info_circle_fill(size=13)],
      )
    Generating => @html.span(style=[MediaTagVisualStyle], [spinner(size=16)])
    Uploading =>
      match thumbnail {
        Some(src) if kind is (Image | Video) => media_tag_thumb(src, true)
        _ => @html.span(style=[MediaTagVisualStyle], [spinner(size=16)])
      }
    Ready | Candidate | Empty =>
      match thumbnail {
        Some(src) if kind is (Image | Video) && !(state is Empty) =>
          media_tag_thumb(src, false)
        _ => media_tag_kind_icon(kind)
      }
  }
}

///|
/// A compact inline reference to a piece of content — the "Image" / "Video"
/// chip that prefixes a conversation bubble or sits inside the composer
/// input. Purely presentational unless `on_click` is given, in which case
/// it renders as a button and brightens on hover.
///
/// ```moonbit nocheck
/// media_tag(kind=Image, thumbnail=thumb_url)
/// media_tag(kind=Video, state=Generating)
/// media_tag(kind=Audio, variant=Inline, state=Candidate)
/// ```
pub fn media_tag(
  kind~ : MediaTagKind,
  label? : String,
  state? : MediaTagState = Ready,
  variant? : MediaTagVariant = Block,
  thumbnail? : String,
  on_click? : @cmd.Cmd,
  aria_label? : String,
  id? : String,
  class? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
) -> @html.Html {
  let text = match label {
    Some(value) => value
    None =>
      match kind {
        Image => "Image"
        Video => "Video"
        Audio => "Audio"
        Text => "Text"
        Group => "Group"
        Timeline => "Timeline"
        Element => "Element"
      }
  }
  let element_attrs = @internal.sumi_attrs(attrs)
    .data_set("slot", "media-tag")
    .data_set("kind", media_tag_kind_name(kind))
    .data_set("state", media_tag_state_name(state))
  if aria_label is Some(label_text) {
    ignore(element_attrs.aria_label(label_text))
  }
  let state_style = if state is Candidate { "opacity:0.5" } else { "" }
  let base = [
    @internal.SumiBoxSizing,
    @internal.SumiFontSans,
    @internal.SumiTextRendering,
    @internal.SumiTransition,
    MediaTagStyle,
  ]
  let variant_style = match variant {
    Block => MediaTagBlockStyle
    Inline => MediaTagInlineStyle
  }
  let children = [media_tag_visual(kind, state, thumbnail), @html.text(text)]
  if on_click is Some(click) {
    ignore(element_attrs.data_set("interactive", "true"))
    @html.button(
      style=@internal.sumi_styles(
        [..base, variant_style, state_style, "cursor:pointer"],
        style,
      ),
      type_="button",
      on_click=click,
      id?,
      class?,
      attrs=element_attrs,
      children,
    )
  } else {
    @html.span(
      style=@internal.sumi_styles([..base, variant_style, state_style], style),
      id?,
      class?,
      attrs=element_attrs,
      children,
    )
  }
}