use super::*;

#[derive(Debug, Parser)]
pub(crate) struct List {
  #[arg(help = "List information for <OUTPOINT>.")]
  outpoint: OutPoint,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Output {
  pub address: Option<Address<NetworkUnchecked>>,
  pub indexed: bool,
  pub inscriptions: Option<Vec<InscriptionId>>,
  pub runes: Option<BTreeMap<SpacedRune, Pile>>,
  pub sat_ranges: Option<Vec<Range>>,
  pub script_pubkey: String,
  pub spent: bool,
  pub transaction: String,
  pub value: u64,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Range {
  pub start: u64,
  pub name: String,
  pub offset: u64,
  pub rarity: Rarity,
  pub end: u64,
  pub size: u64,
}

impl List {
  pub(crate) fn run(self, settings: Settings) -> SubcommandResult {
    let index = Index::open(&settings)?;

    if !index.has_sat_index() {
      bail!("list requires index created with `--index-sats` flag");
    }

    index.update()?;

    ensure! {
      index.is_output_in_active_chain(self.outpoint)?,
      "output not found"
    }

    let (list, _txout) = match index.get_output_info(self.outpoint)? {
      Some((output, txout)) => (output, txout),
      None => return Ok(None),
    };

    Ok(Some(Box::new(Output {
      address: list.address,
      indexed: list.indexed,
      inscriptions: list.inscriptions,
      runes: list.runes,
      sat_ranges: list.sat_ranges.map(output_ranges),
      script_pubkey: list.script_pubkey.to_asm_string(),
      spent: list.spent,
      transaction: list.transaction.to_string(),
      value: list.value,
    })))
  }
}

fn output_ranges(ranges: Vec<(u64, u64)>) -> Vec<Range> {
  let mut offset = 0;
  ranges
    .into_iter()
    .map(|(start, end)| {
      let size = end - start;
      let output = Range {
        end,
        name: Sat(start).name(),
        offset,
        rarity: Sat(start).rarity(),
        size,
        start,
      };

      offset += size;

      output
    })
    .collect()
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn list_ranges() {
    assert_eq!(
      output_ranges(vec![
        (50 * COIN_VALUE, 55 * COIN_VALUE),
        (10, 100),
        (1050000000000000, 1150000000000000),
      ]),
      vec![
        Range {
          end: 55 * COIN_VALUE,
          name: "nvtcsezkbth".to_string(),
          offset: 0,
          rarity: Rarity::Uncommon,
          size: 5 * COIN_VALUE,
          start: 50 * COIN_VALUE,
        },
        Range {
          end: 100,
          name: "nvtdijuwxlf".to_string(),
          offset: 5 * COIN_VALUE,
          rarity: Rarity::Common,
          size: 90,
          start: 10,
        },
        Range {
          end: 1150000000000000,
          name: "gkjbdrhkfqf".to_string(),
          offset: 5 * COIN_VALUE + 90,
          rarity: Rarity::Epic,
          size: 100000000000000,
          start: 1050000000000000,
        }
      ]
    )
  }
}
