use crate::node::script::{edit_node_delta, make_node_delta_changeset};
use lib_ot::core::{
  AttributeEntry, Changeset, NodeDataBuilder, NodeOperation, Transaction, TransactionBuilder,
};
use lib_ot::text_delta::DeltaTextOperationBuilder;

#[test]
fn transaction_compose_update_after_insert_test() {
  let (initial_delta, changeset, _) = make_node_delta_changeset("Hello", " world");
  let node_data = NodeDataBuilder::new("text")
    .insert_delta(initial_delta)
    .build();

  // Modify the same path, the operations will be merged after composing if possible.
  let mut transaction_a = TransactionBuilder::new()
    .insert_node_at_path(0, node_data)
    .build();
  let transaction_b = TransactionBuilder::new()
    .update_node_at_path(0, changeset)
    .build();
  transaction_a.compose(transaction_b).unwrap();

  // The operations are merged into one operation
  assert_eq!(transaction_a.operations.len(), 1);
  assert_eq!(
    transaction_a.to_json().unwrap(),
    r#"{"operations":[{"op":"insert","path":[0],"nodes":[{"type":"text","body":{"delta":[{"insert":"Hello world"}]}}]}]}"#
  );
}

#[test]
fn transaction_compose_multiple_update_test() {
  let (initial_delta, changeset_1, final_delta) = make_node_delta_changeset("Hello", " world");
  let mut transaction = TransactionBuilder::new()
    .insert_node_at_path(
      0,
      NodeDataBuilder::new("text")
        .insert_delta(initial_delta)
        .build(),
    )
    .build();
  let (changeset_2, _) = edit_node_delta(
    &final_delta,
    DeltaTextOperationBuilder::new()
      .retain(final_delta.utf16_target_len)
      .insert("😁")
      .build(),
  );

  let mut other_transaction = Transaction::new();

  // the following two update operations will be merged into one
  let update_1 = TransactionBuilder::new()
    .update_node_at_path(0, changeset_1)
    .build();
  other_transaction.compose(update_1).unwrap();

  let update_2 = TransactionBuilder::new()
    .update_node_at_path(0, changeset_2)
    .build();
  other_transaction.compose(update_2).unwrap();

  let inverted = Transaction::from_operations(other_transaction.operations.inverted());

  // the update operation will be merged into insert operation
  transaction.compose(other_transaction).unwrap();
  assert_eq!(transaction.operations.len(), 1);
  assert_eq!(
    transaction.to_json().unwrap(),
    r#"{"operations":[{"op":"insert","path":[0],"nodes":[{"type":"text","body":{"delta":[{"insert":"Hello world😁"}]}}]}]}"#
  );

  transaction.compose(inverted).unwrap();
  assert_eq!(
    transaction.to_json().unwrap(),
    r#"{"operations":[{"op":"insert","path":[0],"nodes":[{"type":"text","body":{"delta":[{"insert":"Hello"}]}}]}]}"#
  );
}

#[test]
fn transaction_compose_multiple_attribute_test() {
  let delta = DeltaTextOperationBuilder::new().insert("Hello").build();
  let node = NodeDataBuilder::new("text").insert_delta(delta).build();

  let insert_operation = NodeOperation::Insert {
    path: 0.into(),
    nodes: vec![node],
  };

  let mut transaction = Transaction::new();
  transaction.push_operation(insert_operation);

  let new_attribute = AttributeEntry::new("subtype", "bulleted-list");
  let update_operation = NodeOperation::Update {
    path: 0.into(),
    changeset: Changeset::Attributes {
      new: new_attribute.clone().into(),
      old: Default::default(),
    },
  };
  transaction.push_operation(update_operation);
  assert_eq!(
    transaction.to_json().unwrap(),
    r#"{"operations":[{"op":"insert","path":[0],"nodes":[{"type":"text","body":{"delta":[{"insert":"Hello"}]}}]},{"op":"update","path":[0],"changeset":{"attributes":{"new":{"subtype":"bulleted-list"},"old":{}}}}]}"#
  );

  let old_attribute = new_attribute;
  let new_attribute = AttributeEntry::new("subtype", "number-list");
  transaction.push_operation(NodeOperation::Update {
    path: 0.into(),
    changeset: Changeset::Attributes {
      new: new_attribute.into(),
      old: old_attribute.into(),
    },
  });

  assert_eq!(
    transaction.to_json().unwrap(),
    r#"{"operations":[{"op":"insert","path":[0],"nodes":[{"type":"text","body":{"delta":[{"insert":"Hello"}]}}]},{"op":"update","path":[0],"changeset":{"attributes":{"new":{"subtype":"number-list"},"old":{"subtype":"bulleted-list"}}}}]}"#
  );
}
