///|
/// A printable shard card. `index == data_count` is the parity card.
pub(all) struct ShardCard {
  deck : String
  index : Int
  data_count : Int
  width : Int
  original_len : Int
  checksum : Int
  cells : Array[Int]
} derive(Eq, @debug.Debug)

///|
/// Recovery result for a deck of shard cards.
pub(all) enum Recovery {
  Restored(Array[Int])
  NeedCards(Array[Int])
  Damaged(String)
} derive(Eq, @debug.Debug)

///|
/// Build data cards plus one parity card. The parity card can recover one
/// missing data card, and every card carries a checksum for copy mistakes.
pub fn weave(
  deck : String,
  payload : Array[Int],
  data_count : Int,
) -> Array[ShardCard] {
  let clean_count = clamp(data_count, 2, 12)
  let width = max_int(1, ceil_div(payload.length(), clean_count))
  let cards : Array[ShardCard] = []
  for i in 0.. Recovery {
  guard cards.length() > 0 else { return NeedCards([0]) }
  match deck_profile(cards) {
    Some(profile) => {
      let deck = profile.deck
      let data_count = profile.data_count
      let width = profile.width
      let original_len = profile.original_len
      let total = data_count + 1
      let mut parity_present = false
      let buckets : Array[Array[Int]?] = []
      for _ in 0..= 0 && current.index < total else {
          return Damaged("card index is outside the deck")
        }
        guard validate_card(current) else {
          return Damaged(
            "checksum mismatch at card " + current.index.to_string(),
          )
        }
        guard current.cells.length() == width else {
          return Damaged(
            "card width mismatch at card " + current.index.to_string(),
          )
        }
        buckets[current.index] = Some(current.cells)
        if current.index == data_count {
          parity_present = true
        }
      }
      let missing : Array[Int] = []
      for i in 0.. Damaged("card deck is empty or malformed")
  }
}

///|
/// Check the checksum carried by one card.
pub fn validate_card(card : ShardCard) -> Bool {
  card.checksum == checksum(card)
}

///|
/// Return a compact, copy-friendly line for one card.
pub fn render_card(card : ShardCard) -> String {
  "SN1|" +
  card.deck +
  "|" +
  role_name(card) +
  "|" +
  "i=" +
  card.index.to_string() +
  "/" +
  card.data_count.to_string() +
  "|" +
  "w=" +
  card.width.to_string() +
  "|" +
  "n=" +
  card.original_len.to_string() +
  "|" +
  "c=" +
  card.checksum.to_string() +
  "|" +
  "x=" +
  hex_cells(card.cells)
}

///|
/// Render several cards as one printable note sheet.
pub fn render_sheet(cards : Array[ShardCard]) -> String {
  let lines : Array[String] = ["# shard-note"]
  for current in cards {
    lines.push(render_card(current))
  }
  lines.join("\n")
}

///|
/// A small built-in demo payload. It spells "MOONBIT" as byte values.
pub fn demo_payload() -> Array[Int] {
  [77, 79, 79, 78, 66, 73, 84]
}

///|
/// Return the expected validation commands for this package.
pub fn validation_commands() -> Array[String] {
  ["moon check", "moon build", "moon test", "moon run cmd/main"]
}

///|
fn card(
  deck : String,
  index : Int,
  data_count : Int,
  width : Int,
  original_len : Int,
  cells : Array[Int],
) -> ShardCard {
  let normalized = normalize_cells(cells)
  let check = checksum_values(
    index, data_count, width, original_len, normalized,
  )
  {
    deck,
    index,
    data_count,
    width,
    original_len,
    checksum: check,
    cells: normalized,
  }
}

///|
fn deck_profile(cards : Array[ShardCard]) -> ShardCard? {
  guard cards.length() > 0 else { return None }
  Some(cards[0])
}

///|
fn parity_cells(
  cards : Array[ShardCard],
  data_count : Int,
  width : Int,
) -> Array[Int] {
  let out : Array[Int] = []
  for offset in 0.. Array[Int] {
  let out : Array[Int] = []
  for offset in 0.. Array[Int] {
  let out : Array[Int] = []
  for i in 0.. Array[Int] {
  let out : Array[Int] = []
  for offset in 0.. Array[Int] {
  let out : Array[Int] = []
  let take = min_int(payload.length(), original_len)
  for i in 0.. Array[Int] {
  let out : Array[Int] = []
  for cell in cells {
    out.push(normalize_byte(cell))
  }
  out
}

///|
fn checksum(card : ShardCard) -> Int {
  checksum_values(
    card.index,
    card.data_count,
    card.width,
    card.original_len,
    card.cells,
  )
}

///|
fn checksum_values(
  index : Int,
  data_count : Int,
  width : Int,
  original_len : Int,
  cells : Array[Int],
) -> Int {
  let mut value = 1729
  value = checksum_step(value, index)
  value = checksum_step(value, data_count)
  value = checksum_step(value, width)
  value = checksum_step(value, original_len)
  for cell in cells {
    value = checksum_step(value, cell)
  }
  value % 65521
}

///|
fn checksum_step(seed : Int, value : Int) -> Int {
  (seed * 131 + normalize_byte(value) + 17) % 65521
}

///|
fn xor_byte(left : Int, right : Int) -> Int {
  let mut a = normalize_byte(left)
  let mut b = normalize_byte(right)
  let mut bit = 1
  let mut out = 0
  for _step in 0..<8 {
    let abit = a % 2
    let bbit = b % 2
    if abit != bbit {
      out = out + bit
    }
    a = a / 2
    b = b / 2
    bit = bit * 2
  }
  out
}

///|
fn normalize_byte(value : Int) -> Int {
  if value < 0 {
    0
  } else if value > 255 {
    value % 256
  } else {
    value
  }
}

///|
fn hex_cells(cells : Array[Int]) -> String {
  let parts : Array[String] = []
  for cell in cells {
    parts.push(hex_byte(cell))
  }
  parts.join("")
}

///|
fn hex_byte(value : Int) -> String {
  let byte = normalize_byte(value)
  hex_digit(byte / 16) + hex_digit(byte % 16)
}

///|
fn hex_digit(value : Int) -> String {
  match value {
    0 => "0"
    1 => "1"
    2 => "2"
    3 => "3"
    4 => "4"
    5 => "5"
    6 => "6"
    7 => "7"
    8 => "8"
    9 => "9"
    10 => "A"
    11 => "B"
    12 => "C"
    13 => "D"
    14 => "E"
    _ => "F"
  }
}

///|
fn role_name(card : ShardCard) -> String {
  if card.index == card.data_count {
    "parity"
  } else {
    "data"
  }
}

///|
fn ceil_div(left : Int, right : Int) -> Int {
  if right <= 0 {
    0
  } else {
    (left + right - 1) / right
  }
}

///|
fn clamp(value : Int, low : Int, high : Int) -> Int {
  if value < low {
    low
  } else if value > high {
    high
  } else {
    value
  }
}

///|
fn min_int(left : Int, right : Int) -> Int {
  if left < right {
    left
  } else {
    right
  }
}

///|
fn max_int(left : Int, right : Int) -> Int {
  if left > right {
    left
  } else {
    right
  }
}