use std::sync::{Arc, Mutex};

use psdk_father::{
  datawrite::{DataWriteOperation, WriteOptions},
  device::{ConnectedDevice, FakeConnectedDevice, FakeWrite},
};

#[test]
fn test_write_without_chunking_propagates_send_done_option() {
  let device = FakeConnectedDevice::new();
  let operation = DataWriteOperation::new(
    WriteOptions {
      enable_chunk_write: false,
      send_done: false,
      chunk_size: 2,
      callback: None,
    },
    vec![1, 2, 3],
    Arc::new(Mutex::new(device.clone())),
  );

  let reporter = operation.write().unwrap();

  assert!(reporter.ok);
  assert_eq!(
    device.written_chunks(),
    vec![FakeWrite {
      binary: vec![1, 2, 3],
      send_done: false,
    }]
  );
}

#[test]
fn test_chunked_write_sends_done_only_on_final_chunk() {
  let device = FakeConnectedDevice::new();
  let operation = DataWriteOperation::new(
    WriteOptions {
      enable_chunk_write: true,
      send_done: true,
      chunk_size: 2,
      callback: None,
    },
    vec![1, 2, 3, 4, 5],
    Arc::new(Mutex::new(device.clone())),
  );

  let reporter = operation.write().unwrap();

  assert!(reporter.ok);
  assert_eq!(
    device.written_chunks(),
    vec![
      FakeWrite {
        binary: vec![1, 2],
        send_done: false,
      },
      FakeWrite {
        binary: vec![3, 4],
        send_done: false,
      },
      FakeWrite {
        binary: vec![5],
        send_done: true,
      },
    ]
  );
}

#[test]
fn test_default_write_options_use_dart_chunk_size() {
  let device = FakeConnectedDevice::new();
  let operation = DataWriteOperation::new(
    WriteOptions::default(),
    vec![7; 513],
    Arc::new(Mutex::new(device.clone())),
  );

  let reporter = operation.write().unwrap();

  assert!(reporter.ok);
  let chunks = device.written_chunks();
  assert_eq!(chunks.len(), 2);
  assert_eq!(chunks[0].binary.len(), 512);
  assert!(!chunks[0].send_done);
  assert_eq!(chunks[1].binary.len(), 1);
  assert!(chunks[1].send_done);
}

#[test]
fn test_chunked_write_rejects_zero_chunk_size_like_dart() {
  let device = FakeConnectedDevice::new();
  let operation = DataWriteOperation::new(
    WriteOptions {
      enable_chunk_write: true,
      send_done: true,
      chunk_size: 0,
      callback: None,
    },
    vec![1, 2, 3],
    Arc::new(Mutex::new(device.clone())),
  );

  assert!(operation.write().is_err());
  assert!(device.written_chunks().is_empty());
}

#[test]
fn test_fake_device_supports_metadata_cancel_write_and_queued_reads() {
  let device = FakeConnectedDevice::new()
    .with_device_name("TEST-DEVICE")
    .with_device_mac("AA:BB:CC")
    .with_read_data(vec![vec![1, 2], vec![3]]);

  assert_eq!(device.device_name(), "TEST-DEVICE");
  assert_eq!(device.device_mac(), Some("AA:BB:CC".to_string()));
  assert!(!device.cancel_write().unwrap());
  assert_eq!(device.cancel_write_count(), 1);
  assert_eq!(device.read(None).unwrap(), vec![1, 2]);
  assert_eq!(device.read(None).unwrap(), vec![3]);
  assert_eq!(device.read(None).unwrap(), Vec::<u8>::new());
}
