///|
/// Input components for vnode

// =============================================================================
// Input Utilities
// =============================================================================

///|
/// Check if a character is printable (for IME input detection)
pub fn is_printable_char(c : Char) -> Bool {
  let code = c.to_int()
  (code >= 32 && code < 127) || code >= 128
}

///|
/// Check if a string is all printable characters (for IME input detection)
pub fn is_printable_string(s : String) -> Bool {
  for c in s {
    if !is_printable_char(c) {
      return false
    }
  }
  true
}

// =============================================================================
// Input Components
// =============================================================================

///|
/// Input state for visual feedback
pub(all) enum InputState {
  Idle
  Focused
  Editing
  Disabled
}

///|
/// Create an input component
pub fn input(
  value : String,
  id? : String = "",
  placeholder? : String = "",
  state? : InputState = InputState::Idle,
  // Style
  fg? : String = "white",
  bg? : String = "rgb(40,40,50)",
  placeholder_fg? : String = "rgb(100,100,100)",
  focus_border_color? : String = "cyan",
  edit_border_color? : String = "green",
  disabled_fg? : String = "rgb(80,80,80)",
  disabled_bg? : String = "rgb(30,30,35)",
  // Size
  width? : Double = 0.0,
  min_width? : Double = 20.0,
  padding_x? : Double = 1.0,
  padding_y? : Double = 0.0,
) -> TuiNode {
  // Determine display text
  let display_text = if value.length() == 0 {
    match state {
      InputState::Editing => ""
      _ => placeholder
    }
  } else {
    value
  }

  // Determine colors based on state
  let (actual_fg, actual_bg, border_color) = match state {
    InputState::Idle =>
      (
        if value.length() == 0 {
          placeholder_fg
        } else {
          fg
        },
        bg,
        "rgb(80,80,80)",
      )
    InputState::Focused =>
      (
        if value.length() == 0 {
          placeholder_fg
        } else {
          fg
        },
        bg,
        focus_border_color,
      )
    InputState::Editing => (fg, bg, edit_border_color)
    InputState::Disabled => (disabled_fg, disabled_bg, "rgb(60,60,60)")
  }

  // Add cursor indicator when editing
  let text_with_cursor = if state is InputState::Editing {
    display_text + "_"
  } else {
    display_text
  }

  // Build input field with border
  let w = if width > 0.0 { width } else { 0.0 }
  column(
    id~,
    bg=actual_bg,
    border="rounded",
    border_color~,
    min_width~,
    width=w,
    padding_x~,
    padding_y~,
    [text(text_with_cursor, fg=actual_fg)],
  )
}

///|
/// Create an input field without border (inline style)
pub fn input_plain(
  value : String,
  placeholder? : String = "",
  state? : InputState = InputState::Idle,
  fg? : String = "white",
  placeholder_fg? : String = "rgb(100,100,100)",
) -> TuiNode {
  let display_text = if value.length() == 0 {
    match state {
      InputState::Editing => ""
      _ => placeholder
    }
  } else {
    value
  }
  let actual_fg = if value.length() == 0 && !(state is InputState::Editing) {
    placeholder_fg
  } else {
    fg
  }
  let text_with_cursor = if state is InputState::Editing {
    display_text + "_"
  } else {
    display_text
  }
  text(text_with_cursor, fg=actual_fg)
}

// =============================================================================
// Edit functionality
// =============================================================================

///|
/// Configuration for editable input behavior
pub(all) struct EditConfig {
  /// Field name displayed during editing
  field_name : String
  /// Called when value is confirmed
  on_change : (String) -> Unit
  /// Called before editing starts (optional, for setup like disabling mouse)
  on_edit_start : (() -> Unit)?
  /// Called after editing ends (optional, for cleanup like re-enabling mouse)
  on_edit_end : (() -> Unit)?
  /// Called when Ctrl+C is pressed (optional, for force quit)
  on_force_quit : (() -> Unit)?
}

///|
/// Start editing a field with cooked mode (IME support)
/// This exits alternate screen temporarily for proper IME input
/// confirm_on_shift_enter: if true, Shift+Enter confirms and Enter inserts newline
/// esc_cancels: if false, Esc is ignored
pub fn start_edit(
  config : EditConfig,
  current_value : String,
  on_complete : () -> Unit,
  confirm_on_shift_enter? : Bool = false,
  esc_cancels? : Bool = true,
) -> Unit {
  // Call optional setup callback
  match config.on_edit_start {
    Some(f) => f()
    None => ()
  }

  // Start cooked mode input
  @io.start_inline_input_cooked(
    config.field_name,
    current_value,
    fn(result) {
      match result {
        @io.InputResult::Confirmed(new_value) => (config.on_change)(new_value)
        @io.InputResult::Cancelled => ()
        @io.InputResult::ForceQuit => {
          match config.on_force_quit {
            Some(f) => f()
            None => ()
          }
          return
        }
        _ => ()
      }
      match config.on_edit_end {
        Some(f) => f()
        None => ()
      }
      on_complete()
    },
    confirm_on_shift_enter~,
    esc_cancels~,
  )
}

///|
/// Start editing a field inplace (stays in TUI screen)
/// row/col are 1-indexed ANSI coordinates
/// width/height define the input area (supports multi-line wrapping)
/// multiline: if true, Enter can insert newline when confirm_on_shift_enter is true
/// confirm_on_shift_enter: if true, Shift+Enter confirms
/// esc_cancels: if false, Esc is ignored
/// on_lines_change: optional callback (line_count) -> new_height for dynamic resizing
/// on_completion: optional callback (text, cursor_pos) -> completion_suffix for Tab completion
pub fn start_edit_inplace(
  config : EditConfig,
  current_value : String,
  row : Int,
  col : Int,
  width : Int,
  height : Int,
  multiline : Bool,
  on_complete : () -> Unit,
  confirm_on_shift_enter? : Bool = false,
  esc_cancels? : Bool = true,
  on_lines_change? : ((Int) -> Int)? = None,
  on_completion? : ((String, Int) -> String?)? = None,
) -> Unit {
  match config.on_edit_start {
    Some(f) => f()
    None => ()
  }
  @io.start_inplace_input(
    row,
    col,
    width,
    height,
    multiline,
    current_value,
    fn(result) {
      match result {
        @io.InputResult::Confirmed(new_value) => (config.on_change)(new_value)
        @io.InputResult::Cancelled => ()
        @io.InputResult::ForceQuit => {
          match config.on_force_quit {
            Some(f) => f()
            None => ()
          }
          return
        }
        _ => ()
      }
      match config.on_edit_end {
        Some(f) => f()
        None => ()
      }
      on_complete()
    },
    confirm_on_shift_enter~,
    esc_cancels~,
    on_lines_change~,
    on_completion~,
  )
}

///|
fn bounds_to_inplace_area(
  bounds : @events.HitTestResult,
  border : Int,
) -> (Int, Int, Int, Int) {
  let border_size = if border < 0 { 0 } else { border }
  let row = bounds.y + 1 + border_size
  let col = bounds.x + 1 + border_size
  let width = (bounds.width - border_size * 2).max(1)
  let height = (bounds.height - border_size * 2).max(1)
  (row, col, width, height)
}

///|
/// Start editing within bounds (HitTestResult) with border adjustment.
/// bounds.x/y are 0-indexed; row/col for ANSI are 1-indexed.
/// on_completion: optional callback (text, cursor_pos) -> completion_suffix for Tab completion
pub fn start_edit_inplace_in_bounds(
  config : EditConfig,
  current_value : String,
  bounds : @events.HitTestResult,
  multiline : Bool,
  on_complete : () -> Unit,
  confirm_on_shift_enter? : Bool = false,
  esc_cancels? : Bool = true,
  border? : Int = 1,
  on_lines_change? : ((Int) -> Int)? = None,
  on_completion? : ((String, Int) -> String?)? = None,
) -> Unit {
  let (row, col, width, height) = bounds_to_inplace_area(bounds, border)
  start_edit_inplace(
    config,
    current_value,
    row,
    col,
    width,
    height,
    multiline,
    on_complete,
    confirm_on_shift_enter~,
    esc_cancels~,
    on_lines_change~,
    on_completion~,
  )
}

///|
/// Helper to create a standard edit config for form fields
pub fn form_edit_config(
  field_name : String,
  signal : @signals.Signal[String],
  on_edit_start? : (() -> Unit)? = None,
  on_edit_end? : (() -> Unit)? = None,
  on_force_quit? : (() -> Unit)? = None,
) -> EditConfig {
  {
    field_name,
    on_change: fn(value) { signal.set(value) },
    on_edit_start,
    on_edit_end,
    on_force_quit,
  }
}

// =============================================================================
// Textarea
// =============================================================================

///|
/// Create a textarea component (multi-line input)
pub fn textarea(
  value : String,
  id? : String = "",
  placeholder? : String = "",
  state? : InputState = InputState::Idle,
  rows? : Int = 3,
  // Style
  fg? : String = "white",
  bg? : String = "rgb(40,40,50)",
  placeholder_fg? : String = "rgb(100,100,100)",
  focus_border_color? : String = "cyan",
  edit_border_color? : String = "green",
  // Size
  min_width? : Double = 20.0,
  padding_x? : Double = 1.0,
  padding_y? : Double = 0.0,
) -> TuiNode {
  // Determine display text
  let display_text = if value.length() == 0 {
    match state {
      InputState::Editing => ""
      _ => placeholder
    }
  } else {
    value
  }

  // Determine colors based on state
  let (actual_fg, actual_bg, border_color) = match state {
    InputState::Idle =>
      (
        if value.length() == 0 {
          placeholder_fg
        } else {
          fg
        },
        bg,
        "rgb(80,80,80)",
      )
    InputState::Focused =>
      (
        if value.length() == 0 {
          placeholder_fg
        } else {
          fg
        },
        bg,
        focus_border_color,
      )
    InputState::Editing => (fg, bg, edit_border_color)
    InputState::Disabled => ("rgb(80,80,80)", "rgb(30,30,35)", "rgb(60,60,60)")
  }

  // Add cursor indicator when editing
  let text_with_cursor = if state is InputState::Editing {
    display_text + "_"
  } else {
    display_text
  }

  // Build textarea with border
  column(
    id~,
    bg=actual_bg,
    border="rounded",
    border_color~,
    min_width~,
    min_height=rows.to_double() + 2.0, // +2 for border
    padding_x~,
    padding_y~,
    [text(text_with_cursor, fg=actual_fg)],
  )
}