//! Integration tests and additional test coverage for TVM modules

use crate::tvm::*;
use num_bigint::{BigInt, BigUint};
use std::sync::Arc;

/// Helper function to create a cell with specific data
fn create_test_cell(data: Vec<u8>, bit_len: usize) -> Arc<Cell> {
    Arc::new(Cell::with_data(data, bit_len).unwrap())
}

/// Test basic cell operations
#[test]
fn test_cell_operations() {
    let cell = create_test_cell(vec![0xFF, 0x00], 16);
    assert_eq!(cell.bit_len(), 16);
    assert_eq!(cell.data()[0], 0xFF);
    assert_eq!(cell.data()[1], 0x00);

    // Test hash consistency
    let hash1 = cell.hash();
    let hash2 = cell.hash();
    assert_eq!(hash1, hash2);
}

/// Test cell with references
#[test]
fn test_cell_with_references() {
    let mut child_cell = Cell::with_data(vec![0xAA], 8).unwrap();
    let parent_cell = create_test_cell(vec![0xBB], 8);

    child_cell.add_reference(parent_cell.clone()).unwrap();

    assert_eq!(child_cell.reference_count(), 1);
    let ref_cell = child_cell.reference(0).unwrap();
    assert_eq!(ref_cell.hash(), parent_cell.hash());
}

/// Test builder integration
#[test]
fn test_builder_and_cell_integration() {
    let mut builder = Builder::new();

    // Build a complex structure
    let addr = Address::new(0, [1u8; 32]);
    builder.store_address(Some(&addr)).unwrap();

    builder.store_u32(42).unwrap();
    builder.store_bool(true).unwrap();
    builder.store_string("Hello").unwrap();

    let cell = builder.build().unwrap();

    // Verify the cell was built correctly
    assert!(cell.bit_len() > 0);

    // Test that we can create a slice from it
    let slice = Slice::new(cell);
    assert!(slice.remaining_bits() > 0);
}

/// Test BoC serialization/deserialization roundtrip
#[test]
fn test_boc_roundtrip() {
    let mut builder = Builder::new();
    builder.store_u64(0xDEADBEEFCAFEBABE).unwrap();
    builder.store_byte(0xFF).unwrap();

    let original = builder.build().unwrap();

    let boc = serialize_boc(&original, false).unwrap();
    let deserialized = deserialize_boc(&boc).unwrap();

    assert_eq!(original.hash(), deserialized.hash());
}

/// Test BoC with references
#[test]
fn test_boc_with_references() {
    let mut root_builder = Builder::new();
    let mut ref_builder_1 = Builder::new();
    let mut ref_builder_2 = Builder::new();

    // Create reference cells
    ref_builder_1.store_u32(111).unwrap();
    ref_builder_2.store_u32(222).unwrap();

    let ref_cell_1 = ref_builder_1.build().unwrap();
    let ref_cell_2 = ref_builder_2.build().unwrap();

    println!("Ref cell 1 hash: {:?}", ref_cell_1.hash());
    println!("Ref cell 2 hash: {:?}", ref_cell_2.hash());

    // Create root cell with references (clone them for store_ref)
    root_builder.store_u32(999).unwrap();
    root_builder.store_ref(ref_cell_1.clone()).unwrap();
    root_builder.store_ref(ref_cell_2.clone()).unwrap();

    let root = root_builder.build().unwrap();
    assert_eq!(root.reference_count(), 2);

    println!("Original root hash: {:?}", root.hash());

    // Test BoC roundtrip
    let boc = serialize_boc(&root, false).unwrap();
    println!("BoC data length: {}", boc.len());
    println!("BoC data: {:?}", &boc[..20]); // First 20 bytes

    let deserialized = deserialize_boc(&boc).unwrap();
    println!("Deserialized root hash: {:?}", deserialized.hash());

    assert_eq!(root.reference_count(), deserialized.reference_count());

    // Check if the hashes would match with different reference order
    if let (Some(d_ref_0), Some(d_ref_1), Some(o_ref_0), Some(o_ref_1)) = (
        deserialized.reference(0),
        deserialized.reference(1),
        root.reference(0),
        root.reference(1),
    ) {
        println!("Original refs: {:?}, {:?}", o_ref_0.hash(), o_ref_1.hash());
        println!(
            "Deserialized refs: {:?}, {:?}",
            d_ref_0.hash(),
            d_ref_1.hash()
        );
    }

    assert_eq!(root.hash(), deserialized.hash());
}

/// Test address and builder integration
#[test]
fn test_address_builder_integration() {
    let mut addr = Address::new(-1, [0x12; 32]); // masterchain
    addr.set_test_only(true);
    addr.set_bounceable(false);

    let mut builder = Builder::new();
    builder.store_address(Some(&addr)).unwrap();

    let cell = builder.build().unwrap();

    // Verify cell contains address data - should be exactly 267 bits for an address
    assert_eq!(cell.bit_len(), 267);

    // Test that building the same builder produces consistent results
    let mut builder2 = Builder::new();
    builder2.store_address(Some(&addr)).unwrap();
    let cell2 = builder2.build().unwrap();

    assert_eq!(cell.hash(), cell2.hash());
}

/// Test dictionary with builder storage
#[test]
fn test_dict_builder_storage() {
    let mut dict = Dict::new(32);
    dict.set_int_key(1, DictValue::Uint(100, 32)).unwrap();
    dict.set_int_key(2, DictValue::Uint(200, 32)).unwrap();

    // For now, just store the dict using the method
    // TODO: Remove this test as Dict::serialize is not implemented yet
    let mut builder = Builder::new();
    builder.store_bool(true).unwrap(); // Placeholder since Dict::serialize returns None

    let cell = builder.build().unwrap();

    // Dict serialization is not fully implemented yet, so we just test basic functionality
    assert!(cell.bit_len() > 0);
}

/// Test slice operations
#[test]
fn test_slice_operations() {
    let mut builder = CellBuilder::new();
    builder.store_u32(0x12345678).unwrap();
    builder.store_u32(0xABCD).unwrap();
    builder.store_byte(0xFF).unwrap();

    let cell = builder.build().unwrap();

    let mut slice = Slice::new(cell);

    let val32_1 = slice.load_u32().unwrap();
    assert_eq!(val32_1, 0x12345678);

    let val32_2 = slice.load_u32().unwrap();
    assert_eq!(val32_2, 0xABCD);

    let val8 = slice.load_byte().unwrap();
    assert_eq!(val8, 0xFF);
}

/// Test variable-length integer operations
#[test]
fn test_var_uint_operations() {
    let mut builder = Builder::new();

    // Test with a simple value that should work
    builder.store_var_uint(0x42, 4).unwrap(); // Use 4-bit length prefix for simplicity

    let cell = builder.build().unwrap();

    println!(
        "Var uint cell bits: {}, data: {:?}",
        cell.bit_len(),
        cell.data()
    );

    let mut slice = Slice::new(cell);
    let var_uint = slice.load_var_uint(4).unwrap();
    println!("Loaded var_uint: {}", var_uint);

    assert_eq!(var_uint, 0x42);
}

#[test]
fn test_big_uint_roundtrips_for_builder_and_cell_builder() {
    for bits in [0usize, 1, 64, 65, 256, 267, 1023] {
        let value = big_uint_value_for_bits(bits);

        let mut cell_builder = CellBuilder::new();
        cell_builder.store_big_uint(&value, bits).unwrap();
        let mut slice = Slice::new(cell_builder.build().unwrap());
        assert_eq!(slice.load_big_uint(bits).unwrap(), value);

        let mut builder = Builder::new();
        builder.store_big_uint(&value, bits).unwrap();
        let mut slice = builder.to_slice().unwrap();
        assert_eq!(slice.load_big_uint(bits).unwrap(), value);
    }
}

#[test]
fn test_big_int_roundtrips() {
    for bits in [1usize, 8, 65, 257] {
        let min = -(BigInt::from(BigUint::from(1u8) << (bits - 1)));
        let max = BigInt::from((BigUint::from(1u8) << (bits - 1)) - BigUint::from(1u8));
        let mut values = vec![BigInt::from(0), BigInt::from(-1), min, max];
        if bits > 1 {
            values.push(BigInt::from(1));
        }

        for value in values {
            let mut cell_builder = CellBuilder::new();
            cell_builder.store_big_int(&value, bits).unwrap();
            let mut slice = Slice::new(cell_builder.build().unwrap());
            assert_eq!(slice.load_big_int(bits).unwrap(), value);

            let mut builder = Builder::new();
            builder.store_big_int(&value, bits).unwrap();
            let mut slice = builder.to_slice().unwrap();
            assert_eq!(slice.load_big_int(bits).unwrap(), value);
        }
    }
}

#[test]
fn test_big_integer_bit_layout_for_partial_bytes() {
    let mut builder = CellBuilder::new();
    builder.store_big_uint(&BigUint::from(0b101u8), 3).unwrap();
    let cell = builder.build().unwrap();
    assert_eq!(cell.bit_len(), 3);
    assert_eq!(cell.data(), &[0b1010_0000]);

    let mut builder = CellBuilder::new();
    builder
        .store_big_uint(&BigUint::from(0b1_0000_0001u16), 9)
        .unwrap();
    let cell = builder.build().unwrap();
    assert_eq!(cell.bit_len(), 9);
    assert_eq!(cell.data(), &[0b1000_0000, 0b1000_0000]);
}

#[test]
fn test_big_uint_matches_address_bit_layout() {
    let addr = Address::new(-1, [0x5Au8; 32]);
    let mut address_builder = Builder::new();
    address_builder.store_address(Some(&addr)).unwrap();
    let address_cell = address_builder.build().unwrap();

    let mut address_slice = Slice::new(address_cell.clone());
    let address_bits = address_slice.load_big_uint(267).unwrap();

    let mut integer_builder = CellBuilder::new();
    integer_builder.store_big_uint(&address_bits, 267).unwrap();
    let integer_cell = integer_builder.build().unwrap();

    assert_eq!(integer_cell.bit_len(), 267);
    assert_eq!(integer_cell.data(), address_cell.data());
}

#[test]
fn test_big_integer_rejects_overflow_and_zero_width_nonzero() {
    let mut builder = CellBuilder::new();
    assert!(builder.store_big_uint(&BigUint::from(8u8), 3).is_err());
    assert!(builder.store_big_uint(&BigUint::from(1u8), 0).is_err());
    assert!(builder.store_big_int(&BigInt::from(128), 8).is_err());
    assert!(builder.store_big_int(&BigInt::from(-129), 8).is_err());
    assert!(builder.store_big_int(&BigInt::from(1), 0).is_err());
}

#[test]
fn test_big_integer_reader_underflow_errors() {
    let mut builder = CellBuilder::new();
    builder.store_big_uint(&BigUint::from(0b101u8), 3).unwrap();
    let mut slice = Slice::new(builder.build().unwrap());

    assert!(slice.load_big_uint(4).is_err());
    assert_eq!(slice.remaining_bits(), 3);
}

#[test]
fn test_var_big_uint_roundtrips() {
    let values = [
        BigUint::from(0u8),
        BigUint::from(0xABu8),
        BigUint::from_bytes_be(&[0x11; 15]),
        BigUint::from_bytes_be(&[0x22; 100]),
    ];

    for value in values {
        let mut builder = Builder::new();
        builder.store_var_big_uint(&value, 8).unwrap();
        let mut slice = builder.to_slice().unwrap();
        assert_eq!(slice.load_var_big_uint(8).unwrap(), value);
    }
}

#[test]
fn test_var_big_uint_rejects_length_overflow() {
    let value = BigUint::from_bytes_be(&[0xFF; 16]);
    let mut builder = Builder::new();
    assert!(builder.store_var_big_uint(&value, 4).is_err());
}

#[test]
fn test_coins_reject_16_byte_value() {
    let mut builder = Builder::new();
    assert!(builder.store_coins(u128::MAX).is_err());
}

fn big_uint_value_for_bits(bits: usize) -> BigUint {
    match bits {
        0 => BigUint::from(0u8),
        1 => BigUint::from(1u8),
        _ => (BigUint::from(1u8) << (bits - 1)) | BigUint::from(0xA5u8),
    }
}

/// Test external address handling
#[test]
fn test_external_address_operations() {
    let ext_addr = ExternalAddress::new(Some(0x12345678ABCDEF00), Some(64));
    let mut builder = Builder::new();

    builder.store_external_address(&ext_addr).unwrap();
    let cell = builder.build().unwrap();

    // Should have data representing the external address
    assert!(cell.bit_len() > 0);
}

/// Test snake string functionality
#[test]
fn test_snake_string_integration() {
    let long_string = "This is a very long string that should be split across multiple cells when stored as a snake string. ".repeat(10);

    let mut builder = Builder::new();
    builder.store_snake_string(&long_string, false).unwrap();

    let cell = builder.build().unwrap();

    // Should have created references due to length
    assert!(cell.reference_count() > 0);
}

/// Test hash consistency across operations
#[test]
fn test_hash_consistency() {
    // Create the same cell in different ways and ensure same hash

    // Method 1: Direct cell creation
    let cell1 = create_test_cell(vec![0x11, 0x22, 0x33], 24);

    // Method 2: Via builder
    let mut builder = CellBuilder::new();
    builder.store_byte(0x11).unwrap();
    builder.store_byte(0x22).unwrap();
    builder.store_byte(0x33).unwrap();
    let cell2 = builder.build().unwrap();

    // Method 3: Via high-level builder
    let mut h_builder = Builder::new();
    h_builder.store_bytes(&[0x11, 0x22, 0x33]).unwrap();
    let cell3 = h_builder.build().unwrap();

    assert_eq!(cell1.hash(), cell2.hash());
    assert_eq!(cell2.hash(), cell3.hash());
}

/// Test edge cases and error conditions
#[test]
fn test_edge_cases() {
    // Empty cell
    let empty_cell = Cell::new();
    assert_eq!(empty_cell.bit_len(), 0);

    // Cell with maximum bits
    let max_data = vec![0xFF; MAX_CELL_BITS.div_ceil(8)];
    let max_cell = Cell::with_data(max_data, MAX_CELL_BITS).unwrap();
    assert_eq!(max_cell.bit_len(), MAX_CELL_BITS);

    // Test slice boundary conditions
    let mut builder = Builder::new();
    builder.store_bit(true).unwrap();
    let single_bit_cell = builder.build().unwrap();

    let mut slice = Slice::new(single_bit_cell);
    assert_eq!(slice.remaining_bits(), 1);
    assert!(slice.load_bit().unwrap());
    assert_eq!(slice.remaining_bits(), 0);
    assert!(slice.is_empty());
}

/// Test multiple reference management
#[test]
fn test_multiple_references() {
    let mut root_cell = Cell::with_data(vec![0x00], 8).unwrap();

    // Add maximum allowed references
    for i in 0..MAX_CELL_REFS {
        let ref_cell = create_test_cell(vec![i as u8], 8);
        root_cell.add_reference(ref_cell).unwrap();
    }

    assert_eq!(root_cell.reference_count(), MAX_CELL_REFS);

    // Verify we can't add more
    let extra_cell = create_test_cell(vec![0xFF], 8);
    assert!(root_cell.add_reference(extra_cell).is_err());
}

/// Test BoC with different CRC options
#[test]
fn test_boc_crc_options() {
    let mut builder = Builder::new();
    builder.store_u64(0xDEADBEEFCAFEBABE).unwrap();
    let cell = builder.build().unwrap();

    // Without CRC
    let boc_no_crc = serialize_boc(&cell, false).unwrap();

    // With CRC
    let boc_with_crc = serialize_boc(&cell, true).unwrap();

    // With CRC should be longer
    assert!(boc_with_crc.len() > boc_no_crc.len());

    // Both should deserialize correctly
    let deserialized_no_crc = deserialize_boc(&boc_no_crc).unwrap();
    let deserialized_with_crc = deserialize_boc(&boc_with_crc).unwrap();

    assert_eq!(cell.hash(), deserialized_no_crc.hash());
    assert_eq!(cell.hash(), deserialized_with_crc.hash());
}

/// Test hex/base64 BoC conversion
#[test]
fn test_boc_conversions() {
    let mut builder = Builder::new();
    builder.store_u32(0xDEADBEEF).unwrap();
    let cell = builder.build().unwrap();

    // Test hex conversion
    let hex = boc_to_hex(&cell, false).unwrap();
    let from_hex = hex_to_boc(&hex).unwrap();
    assert_eq!(cell.hash(), from_hex.hash());

    // Test base64 conversion
    let b64 = boc_to_base64(&cell, false).unwrap();
    let from_b64 = base64_to_boc(&b64).unwrap();
    assert_eq!(cell.hash(), from_b64.hash());
}
