// Copyright 2026 Ling71671
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Semantic intent category of dialog modal.
pub(all) enum DialogKind {
  Info
  Warning
  Danger
  Confirm
} derive(Eq, Debug)

///|
pub fn DialogKind::info() -> DialogKind {
  Info
}

///|
pub fn DialogKind::warning() -> DialogKind {
  Warning
}

///|
pub fn DialogKind::danger() -> DialogKind {
  Danger
}

///|
pub fn DialogKind::confirm() -> DialogKind {
  Confirm
}

///|
/// Result returned from dialog interaction on the current frame.
pub(all) enum DialogResult {
  Confirmed
  Cancelled
  Open
} derive(Eq, Debug)

///|
fn wrap_dialog_message(
  ctx : UIContext,
  message : String,
  max_w : Double,
  font_size : Double,
) -> Array[String] {
  let lines : Array[String] = []
  let raw_paragraphs = message.split("\n")
  for p in raw_paragraphs {
    let p_str = p.to_owned()
    if p_str.is_empty() {
      lines.push("")
      continue
    }
    let mut cur = ""
    let mut i = 0
    let len = p_str.length()
    while i < len {
      let next_i = @core.next_char_boundary(p_str, i)
      let ch = @core.string_slice(p_str, i, next_i)
      let test_str = cur + ch
      let w = ctx.measure_text(test_str, font_size).x
      if w > max_w && !cur.is_empty() {
        lines.push(cur)
        cur = ch
      } else {
        cur = test_str
      }
      i = next_i
    }
    if !cur.is_empty() {
      lines.push(cur)
    }
  }
  lines
}

///|
/// Standalone modal dialog widget supporting fluent configuration.
pub struct Dialog {
  id_salt : String
  title : String
  message : String
  kind : DialogKind
  confirm_label : String
  cancel_label : String
  width : Double
} derive(Eq, Debug)

///|
/// Create a new dialog configuration.
pub fn Dialog::new(
  id_salt : String,
  title : String,
  message : String,
) -> Dialog {
  {
    id_salt,
    title,
    message,
    kind: DialogKind::Info,
    confirm_label: "Confirm",
    cancel_label: "Cancel",
    width: 440.0,
  }
}

///|
/// Configure semantic kind (Info, Warning, Danger, Confirm).
pub fn Dialog::kind(self : Dialog, kind : DialogKind) -> Dialog {
  { ..self, kind, }
}

///|
/// Configure confirm action button label.
pub fn Dialog::confirm_label(self : Dialog, confirm_label : String) -> Dialog {
  { ..self, confirm_label, }
}

///|
/// Configure cancel action button label.
pub fn Dialog::cancel_label(self : Dialog, cancel_label : String) -> Dialog {
  { ..self, cancel_label, }
}

///|
/// Configure dialog card width.
pub fn Dialog::width(self : Dialog, width : Double) -> Dialog {
  { ..self, width, }
}

///|
/// Show the dialog modal and return the user interaction result.
pub fn Dialog::show(self : Dialog, ctx : UIContext) -> DialogResult {
  dialog(
    ctx,
    self.id_salt,
    self.title,
    self.message,
    kind=self.kind,
    confirm_label=self.confirm_label,
    cancel_label=self.cancel_label,
    width=self.width,
  )
}

///|
/// Render a modal confirmation dialog in Studio Light porcelain style.
pub fn dialog(
  ctx : UIContext,
  id_salt : String,
  title : String,
  message : String,
  kind? : DialogKind = Info,
  confirm_label? : String = "Confirm",
  cancel_label? : String = "Cancel",
  width? : Double = 440.0,
  viewport_size? : @math.Vec2 = @math.Vec2::zero(),
) -> DialogResult {
  let scale = ctx.style.scale
  let clip_opt = ctx.current_clip()
  let vp_w = if viewport_size.x > 0.0 {
    viewport_size.x
  } else if ctx.available_width() > 0.0 {
    ctx.available_width()
  } else {
    match clip_opt {
      Some(c) => if c.w > 0.0 { c.x + c.w } else { 800.0 * scale }
      None => 800.0 * scale
    }
  }
  let vp_h = if viewport_size.y > 0.0 {
    viewport_size.y
  } else {
    match clip_opt {
      Some(c) => if c.h > 0.0 { c.y + c.h } else { 600.0 * scale }
      None => 600.0 * scale
    }
  }
  let scrim_rect = @math.Rect::new(0.0, 0.0, vp_w, vp_h)
  ctx.block_hover(scrim_rect)

  let cancel_id = ctx.id_stack.derive_id(id_salt + "::cancel")
  let confirm_id = ctx.id_stack.derive_id(id_salt + "::confirm")

  // Modal focus trapping: clear previous background focusable IDs and constrain to dialog buttons
  ctx.clear_focusable()
  ctx.register_focusable(cancel_id)
  ctx.register_focusable(confirm_id)

  if ctx.focused_id() != cancel_id && ctx.focused_id() != confirm_id {
    ctx.request_focus(confirm_id)
  }

  let eff_w = if width * scale > vp_w - 32.0 * scale {
    if vp_w > 32.0 * scale {
      vp_w - 32.0 * scale
    } else {
      vp_w
    }
  } else {
    width * scale
  }

  let text_max_w = eff_w - 84.0 * scale
  let msg_font_sz = ctx.style.font_normal * scale
  let msg_lines = wrap_dialog_message(ctx, message, text_max_w, msg_font_sz)
  let msg_line_h = 20.0 * scale
  let msg_total_h = msg_lines.length().to_double() * msg_line_h
  let pad_top = 24.0 * scale
  let title_sz = ctx.measure_text(title, ctx.style.font_large * scale)
  let title_h = if title_sz.y > 0.0 {
    title_sz.y
  } else {
    ctx.style.font_large * scale
  }
  let msg_gap = 10.0 * scale
  let btn_radius = ctx.style.radius_md * scale
  let btn_h = ctx.style.control_h_md * scale
  let btn_w = ctx.style.control_h_lg * 2.0 * scale
  let btn_pad_right = 16.0 * scale
  let btn_gap = 8.0 * scale
  let btn_margin_top = 18.0 * scale
  let btn_pad_y = 16.0 * scale

  let content_needed_h = pad_top +
    title_h +
    msg_gap +
    msg_total_h +
    btn_margin_top +
    btn_h +
    btn_pad_y
  let min_h = 160.0 * scale
  let max_allowed_h = if vp_h > 80.0 * scale {
    vp_h - 40.0 * scale
  } else {
    min_h
  }
  let card_h = if content_needed_h < min_h {
    min_h
  } else if content_needed_h > max_allowed_h {
    max_allowed_h
  } else {
    content_needed_h
  }

  let card_radius = ctx.style.radius_xl * scale
  let card_x = (vp_w - eff_w) * 0.5
  let card_y = (vp_h - card_h) * 0.5
  let card_rect = @math.Rect::new(card_x, card_y, eff_w, card_h)

  ctx.begin_foreground()

  // 1. Fullscreen Backdrop Scrim
  let theme = ctx.theme
  ctx.painter().add_rect(scrim_rect, theme.bg_inverse.with_alpha(128), 0.0)

  // 2. Multi-layer Studio Light Shadows
  ctx
  .painter()
  .add_rect(
    @math.Rect::new(card_x, card_y + 4.0 * scale, eff_w, card_h),
    theme.shadow,
    card_radius,
  )
  ctx
  .painter()
  .add_rect(
    @math.Rect::new(card_x, card_y + 12.0 * scale, eff_w, card_h),
    theme.shadow,
    card_radius,
  )

  // 3. Card Base & Outline
  ctx.painter().add_rect(card_rect, theme.bg_surface, card_radius)
  ctx
  .painter()
  .add_rect_stroke(card_rect, theme.border_default, 1.0 * scale, card_radius)

  // 4. Semantic Badge Icon
  let icon_center = @math.Vec2::new(
    card_x + 32.0 * scale,
    card_y + 36.0 * scale,
  )
  let (badge_bg, badge_sym, badge_fg) = match kind {
    Info => (theme.accent_soft, "i", theme.accent_primary)
    Warning => (theme.warning.with_alpha(40), "!", theme.warning)
    Danger => (theme.danger.with_alpha(40), "x", theme.danger)
    Confirm => (theme.accent_soft, "?", theme.accent_primary)
  }

  ctx.painter().add_circle(icon_center, 16.0 * scale, badge_bg)
  let sym_offset = ctx.measure_text(badge_sym, ctx.style.font_normal * scale)
  let sym_pos = @math.Vec2::new(
    icon_center.x - sym_offset.x * 0.5,
    icon_center.y - sym_offset.y * 0.5,
  )
  ctx
  .painter()
  .add_text(sym_pos, badge_sym, ctx.style.font_normal * scale, badge_fg)

  // 5. Title & Message Text
  let title_pos = @math.Vec2::new(card_x + 60.0 * scale, card_y + pad_top)
  ctx
  .painter()
  .add_text(title_pos, title, ctx.style.font_large * scale, theme.text_strong)

  let btn_y = card_y + card_h - btn_h - btn_pad_y
  let msg_start_y = card_y + pad_top + title_h + msg_gap
  for idx = 0; idx < msg_lines.length(); idx = idx + 1 {
    let line_y = msg_start_y + idx.to_double() * msg_line_h
    if line_y + msg_line_h <= btn_y - 6.0 * scale {
      ctx
      .painter()
      .add_text(
        @math.Vec2::new(card_x + 60.0 * scale, line_y),
        msg_lines[idx],
        msg_font_sz,
        theme.text_secondary,
      )
    }
  }

  // 6. Action Footer Buttons
  let confirm_rect = @math.Rect::new(
    card_x + eff_w - btn_w - btn_pad_right,
    btn_y,
    btn_w,
    btn_h,
  )
  let cancel_rect = @math.Rect::new(
    confirm_rect.x - btn_w - btn_gap,
    btn_y,
    btn_w,
    btn_h,
  )

  let cancel_hovered = ctx.is_hovered(cancel_rect)
  let confirm_hovered = ctx.is_hovered(confirm_rect)
  if cancel_hovered || confirm_hovered {
    ctx.set_cursor_icon("pointer")
  }
  let cancel_bg = if cancel_hovered { theme.bg_hover } else { theme.bg_subtle }
  ctx.painter().add_rect(cancel_rect, cancel_bg, btn_radius)
  ctx
  .painter()
  .add_rect_stroke(cancel_rect, theme.border_default, 1.0 * scale, btn_radius)
  if ctx.has_focus(cancel_id) {
    ctx
    .painter()
    .add_rect_stroke(
      cancel_rect.expand(2.0 * scale),
      theme.border_focus,
      1.5 * scale,
      btn_radius + 2.0 * scale,
    )
  }
  let cancel_sz = ctx.measure_text(cancel_label, ctx.style.font_normal * scale)
  let cancel_text_pos = @math.Vec2::new(
    cancel_rect.x + (cancel_rect.w - cancel_sz.x) * 0.5,
    cancel_rect.y + (cancel_rect.h - cancel_sz.y) * 0.5,
  )
  ctx
  .painter()
  .add_text(
    cancel_text_pos,
    cancel_label,
    ctx.style.font_normal * scale,
    theme.text_body,
  )
  let cancel_clicked = cancel_hovered && ctx.input.mouse_pressed
  let confirm_bg = match kind {
    Danger => theme.danger
    _ => if confirm_hovered { theme.accent_hover } else { theme.accent_primary }
  }
  ctx.painter().add_rect(confirm_rect, confirm_bg, btn_radius)
  if ctx.has_focus(confirm_id) {
    ctx
    .painter()
    .add_rect_stroke(
      confirm_rect.expand(2.0 * scale),
      theme.border_focus,
      1.5 * scale,
      btn_radius + 2.0 * scale,
    )
  }
  let confirm_sz = ctx.measure_text(
    confirm_label,
    ctx.style.font_normal * scale,
  )
  let confirm_text_pos = @math.Vec2::new(
    confirm_rect.x + (confirm_rect.w - confirm_sz.x) * 0.5,
    confirm_rect.y + (confirm_rect.h - confirm_sz.y) * 0.5,
  )
  ctx
  .painter()
  .add_text(
    confirm_text_pos,
    confirm_label,
    ctx.style.font_normal * scale,
    theme.text_inverse,
  )
  let confirm_clicked = confirm_hovered && ctx.input.mouse_pressed

  // 7. Interaction and Result Decision
  let esc_pressed = ctx.input.key_pressed(Key::Escape)
  let enter_pressed = ctx.input.key_pressed(Key::Enter)
  let space_pressed = ctx.input.key_pressed(Key::Space)
  let backdrop_clicked = ctx.input.mouse_pressed &&
    !card_rect.contains(ctx.input.mouse_pos)

  let cancel_key = ctx.has_focus(cancel_id) && (enter_pressed || space_pressed)
  let confirm_key = ctx.has_focus(confirm_id) &&
    (enter_pressed || space_pressed)

  let result = if confirm_clicked ||
    confirm_key ||
    (enter_pressed && !ctx.has_focus(cancel_id)) {
    if enter_pressed {
      ctx.input.consume_key(Key::Enter)
    }
    if space_pressed {
      ctx.input.consume_key(Key::Space)
    }
    DialogResult::Confirmed
  } else if cancel_clicked || cancel_key || esc_pressed || backdrop_clicked {
    if esc_pressed {
      ctx.input.consume_key(Key::Escape)
    }
    if enter_pressed {
      ctx.input.consume_key(Key::Enter)
    }
    if space_pressed {
      ctx.input.consume_key(Key::Space)
    }
    DialogResult::Cancelled
  } else {
    DialogResult::Open
  }

  ctx.end_foreground()
  result
}