///|
pub(all) enum MarketRefreshAction {
  RunNow
  WaitForBarClose
  AwaitExternalSnapshot
  ReviewTimeframe
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn MarketRefreshAction::label(self : MarketRefreshAction) -> String {
  match self {
    RunNow => "run_now"
    WaitForBarClose => "wait_for_bar_close"
    AwaitExternalSnapshot => "await_external_snapshot"
    ReviewTimeframe => "review_timeframe"
  }
}

///|
pub(all) struct MarketRefreshInput {
  request : MarketSourceRequest
  now_ms : Int64
  newest_bar_open_ms : Int64?
  head_bar_closed : Bool
  user_refresh_ms : Int
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct MarketRefreshPlan {
  request : MarketSourceRequest
  timeframe_seconds : Int?
  effective_refresh_ms : Int
  snapshot_cache_ttl_seconds : Int
  wait_seconds : Int
  action : MarketRefreshAction
  safe_to_request_snapshot : Bool
  can_run_analysis_now : Bool
  summary : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn market_refresh_input(
  request : MarketSourceRequest,
  now_ms? : Int64 = 0L,
  newest_bar_open_ms? : Int64,
  head_bar_closed? : Bool = true,
  user_refresh_ms? : Int = 1000,
) -> MarketRefreshInput {
  {
    request,
    now_ms: if now_ms == 0L {
      request.captured_at_ms
    } else {
      now_ms
    },
    newest_bar_open_ms,
    head_bar_closed,
    user_refresh_ms,
  }
}

///|
pub fn timeframe_seconds(timeframe : String) -> Int? {
  match timeframe.trim().to_owned().to_lower() {
    "1m" => Some(60)
    "2m" => Some(120)
    "3m" => Some(180)
    "5m" => Some(300)
    "15m" => Some(900)
    "30m" => Some(1800)
    "1h" => Some(3600)
    "2h" => Some(7200)
    "4h" => Some(14400)
    "1d" => Some(86400)
    "1w" => Some(604800)
    _ => None
  }
}

///|
fn http_poll_source(source : @domain.DataSource) -> Bool {
  source is AkShare || source is Yfinance
}

///|
fn effective_refresh_interval_ms(
  source : @domain.DataSource,
  timeframe : String,
  user_ms : Int,
) -> Int {
  let mut interval = user_ms
  if interval < 500 {
    interval = 500
  }
  if http_poll_source(source) && interval < 2500 {
    interval = 2500
  }
  if http_poll_source(source) &&
    timeframe.trim().to_owned().to_lower() == "1d" &&
    interval < 3000 {
    interval = 3000
  }
  interval
}

///|
fn snapshot_cache_ttl_seconds(timeframe : String) -> Int {
  match timeframe.trim().to_owned().to_lower() {
    "1m" => 4
    "1d" | "1w" | "1mth" => 12
    _ => 8
  }
}

///|
fn seconds_until_close(
  now_ms : Int64,
  open_ms : Int64,
  duration_seconds : Int,
) -> Int {
  let duration_ms = duration_seconds.to_int64() * 1000L
  let elapsed_ms = now_ms - open_ms
  if elapsed_ms == 0L {
    duration_seconds
  } else {
    let remainder = elapsed_ms.mod(duration_ms)
    if remainder == 0L {
      if elapsed_ms > 0L {
        0
      } else {
        duration_seconds
      }
    } else {
      let remaining_ms = duration_ms - remainder
      ((remaining_ms + 999L) / 1000L).to_int()
    }
  }
}

///|
fn forming_wait_seconds(input : MarketRefreshInput, duration : Int?) -> Int {
  match (input.head_bar_closed, input.newest_bar_open_ms, duration) {
    (false, Some(open_ms), Some(seconds)) =>
      seconds_until_close(input.now_ms, open_ms, seconds)
    _ => 0
  }
}

///|
fn refresh_action(
  input : MarketRefreshInput,
  duration : Int?,
  wait_seconds : Int,
) -> MarketRefreshAction {
  match duration {
    None => ReviewTimeframe
    Some(_) =>
      if !input.head_bar_closed && wait_seconds > 0 {
        WaitForBarClose
      } else if input.request.data_source is Fixture {
        RunNow
      } else {
        AwaitExternalSnapshot
      }
  }
}

///|
fn refresh_summary(plan : MarketRefreshPlan) -> String {
  match plan.action {
    RunNow =>
      "refresh plan ready: fixture evidence can run analysis now for \{plan.request.symbol} \{plan.request.timeframe}"
    WaitForBarClose =>
      "refresh plan waiting \{plan.wait_seconds}s for closed-bar evidence before requesting \{plan.request.symbol} \{plan.request.timeframe}"
    AwaitExternalSnapshot =>
      "refresh plan ready for external snapshot request; analysis awaits submitted evidence for \{plan.request.symbol} \{plan.request.timeframe}"
    ReviewTimeframe =>
      "refresh plan needs timeframe review for \{plan.request.timeframe}"
  }
}

///|
pub fn prepare_market_refresh_plan(
  input : MarketRefreshInput,
) -> MarketRefreshPlan {
  let duration = timeframe_seconds(input.request.timeframe)
  let wait_seconds = forming_wait_seconds(input, duration)
  let action = refresh_action(input, duration, wait_seconds)
  let plan : MarketRefreshPlan = {
    request: input.request,
    timeframe_seconds: duration,
    effective_refresh_ms: effective_refresh_interval_ms(
      input.request.data_source,
      input.request.timeframe,
      input.user_refresh_ms,
    ),
    snapshot_cache_ttl_seconds: snapshot_cache_ttl_seconds(
      input.request.timeframe,
    ),
    wait_seconds,
    action,
    safe_to_request_snapshot: action is RunNow ||
    action is AwaitExternalSnapshot,
    can_run_analysis_now: action is RunNow,
    summary: "",
  }
  { ..plan, summary: refresh_summary(plan) }
}