use rbs::Value;
use rbs::value::Table;
use rbs::value::map::ValueMap;
use serde_json;

#[test]
fn test_table_basic() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_row(vec![Value::I32(1), Value::String("Alice".to_string())]);
    table.add_row(vec![Value::I32(2), Value::String("Bob".to_string())]);

    let value = Value::Table(table.clone());

    assert!(value.is_table());
    let t = value.as_table().unwrap();
    assert_eq!(t.column_count(), 2);
    assert_eq!(t.row_count(), 2);
    assert_eq!(t.get(0, 0), Some(&Value::I32(1)));
    assert_eq!(t.get(0, 1), Some(&Value::String("Alice".to_string())));
    assert_eq!(t.get(1, 0), Some(&Value::I32(2)));
    assert_eq!(t.get(1, 1), Some(&Value::String("Bob".to_string())));

    // Test row()
    let row0 = t.row(0);
    assert_eq!(row0.as_array().unwrap().len(), 2);
    assert_eq!(row0.as_array().unwrap()[0], Value::I32(1));
    assert_eq!(row0.as_array().unwrap()[1], Value::String("Alice".to_string()));

    let row1 = t.row(1);
    assert_eq!(row1.as_array().unwrap()[0], Value::I32(2));
    assert_eq!(row1.as_array().unwrap()[1], Value::String("Bob".to_string()));

    // row() out of bounds returns Null
    let row2 = t.row(2);
    assert!(row2.is_null());

    // Test column()
    let col_id = t.column("id");
    assert_eq!(col_id.as_array().unwrap().len(), 2);
    assert_eq!(col_id.as_array().unwrap()[0], Value::I32(1));
    assert_eq!(col_id.as_array().unwrap()[1], Value::I32(2));

    let col_name = t.column("name");
    assert_eq!(col_name.as_array().unwrap()[0], Value::String("Alice".to_string()));
    assert_eq!(col_name.as_array().unwrap()[1], Value::String("Bob".to_string()));

    // column() with non-existent name returns Null
    let col_unknown = t.column("unknown");
    assert!(col_unknown.is_null());
}

#[test]
fn test_table_serialize() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_row(vec![Value::I32(1), Value::String("Alice".to_string())]);
    table.add_row(vec![Value::I32(2), Value::String("Bob".to_string())]);

    let value = Value::Table(table);
    let serialized = serde_json::to_string(&value).unwrap();

    // Should contain columns and rows
    assert!(!serialized.contains("__table__"));
    assert!(serialized.contains("columns"));
    assert!(serialized.contains("rows"));
    assert!(serialized.contains("\"id\""));
    assert!(serialized.contains("\"name\""));
    assert!(serialized.contains("[1,\"Alice\"]"));
    assert!(serialized.contains("[2,\"Bob\"]"));
}

#[test]
fn test_table_deserialize() {
    // No __table__ marker needed - just columns + rows
    let json = r#"{"columns":["id","name"],"rows":[[1,"Alice"],[2,"Bob"]]}"#;
    let value: Value = serde_json::from_str(json).unwrap();
    assert!(value.is_table());
    let table = value.as_table().unwrap();

    assert_eq!(table.columns.len(), 2);
    assert_eq!(table.rows.len(), 2);
    // JSON numbers deserialize as U64 by default
    assert_eq!(table.rows[0], vec![Value::U64(1), Value::String("Alice".to_string())]);
    assert_eq!(table.rows[1], vec![Value::U64(2), Value::String("Bob".to_string())]);
}

#[test]
fn test_table_roundtrip() {
    let mut original = Table::new();
    original.add_column(Value::String("id".to_string()));
    original.add_column(Value::String("name".to_string()));
    original.add_column(Value::String("status".to_string()));
    // Use U64 to match JSON deserialization behavior
    original.add_row(vec![Value::U64(1), Value::String("Alice".to_string()), Value::U64(1)]);
    original.add_row(vec![Value::U64(2), Value::String("Bob".to_string()), Value::U64(0)]);
    original.add_row(vec![Value::U64(3), Value::String("Charlie".to_string()), Value::U64(1)]);

    let value = Value::Table(original.clone());
    let json = serde_json::to_string(&value).unwrap();
    // Should not contain __table__ marker
    assert!(!json.contains("__table__"));
    let recovered: Value = serde_json::from_str(&json).unwrap();
    let recovered_table = recovered.as_table().unwrap();

    assert_eq!(original.columns, recovered_table.columns);
    assert_eq!(original.rows, recovered_table.rows);
}

#[test]
fn test_table_with_nulls() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_row(vec![Value::I32(1), Value::Null]);
    table.add_row(vec![Value::Null, Value::String("Bob".to_string())]);

    let value = Value::Table(table);
    let json = serde_json::to_string(&value).unwrap();
    let recovered: Value = serde_json::from_str(&json).unwrap();
    let t = recovered.as_table().unwrap();

    assert_eq!(t.rows[0][1], Value::Null);
    assert_eq!(t.rows[1][0], Value::Null);
}

#[test]
fn test_table_len_and_is_empty() {
    let mut table = Table::new();
    let value = Value::Table(table.clone());
    assert!(value.is_empty());
    assert_eq!(value.len(), 0);

    table.add_column(Value::String("id".to_string()));
    table.add_row(vec![Value::I32(1)]);
    let value = Value::Table(table);
    assert!(!value.is_empty());
    assert_eq!(value.len(), 1);
}

#[test]
fn test_table_display() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_row(vec![Value::I32(1), Value::String("Alice".to_string())]);

    let value = Value::Table(table);
    let display = format!("{}", value);

    // Should be compact JSON with columns and rows
    assert!(display.contains("columns"));
    assert!(display.contains("rows"));
    assert!(display.contains("id"));
    assert!(display.contains("name"));
}

#[test]
fn test_table_into_table() {
    let mut table = Table::new();
    table.add_column(Value::String("x".to_string()));
    table.add_row(vec![Value::I32(42)]);

    let value = Value::Table(table.clone());
    let recovered = value.into_table().unwrap();
    assert_eq!(recovered.columns, table.columns);
    assert_eq!(recovered.rows, table.rows);

    let not_table = Value::String("test".to_string());
    assert!(not_table.into_table().is_none());
}

#[test]
fn test_table_hash() {
    let mut t1 = Table::new();
    t1.add_column(Value::String("id".to_string()));
    t1.add_row(vec![Value::I32(1)]);

    let mut t2 = Table::new();
    t2.add_column(Value::String("id".to_string()));
    t2.add_row(vec![Value::I32(1)]);

    let mut t3 = Table::new();
    t3.add_column(Value::String("id".to_string()));
    t3.add_row(vec![Value::I32(2)]);

    let v1 = Value::Table(t1);
    let v2 = Value::Table(t2);
    let v3 = Value::Table(t3);

    assert_eq!(v1, v2);
    assert_ne!(v1, v3);
}

#[test]
fn test_table_with_complex_types() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("data".to_string()));
    table.add_row(vec![
        Value::I32(1),
        Value::Array(vec![Value::String("a".to_string()), Value::String("b".to_string())]),
    ]);

    let value = Value::Table(table);
    let json = serde_json::to_string(&value).unwrap();
    let recovered: Value = serde_json::from_str(&json).unwrap();
    let t = recovered.as_table().unwrap();

    if let Value::Array(arr) = &t.rows[0][1] {
        assert_eq!(arr.len(), 2);
        assert_eq!(arr[0], Value::String("a".to_string()));
    } else {
        panic!("Expected array");
    }
}

#[test]
fn test_table_deserialize_to_struct() {
    #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
    struct User {
        id: u64,
        name: String,
        email: String,
        status: u32,
    }

    // JSON in table format
    let json = r#"{
        "columns": ["id", "name", "email", "status"],
        "rows": [
            [1, "Alice", "alice@example.com", 1],
            [2, "Bob", "bob@example.com", 0]
        ]
    }"#;

    // Deserialize to Value (Table)
    let value: Value = serde_json::from_str(json).unwrap();
    // Convert directly to struct Vec using to_vec()
    let users: Vec<User> = rbs::from_value(value).unwrap();

    assert_eq!(users.len(), 2);
    assert_eq!(users[0], User {
        id: 1,
        name: "Alice".to_string(),
        email: "alice@example.com".to_string(),
        status: 1,
    });
    assert_eq!(users[1], User {
        id: 2,
        name: "Bob".to_string(),
        email: "bob@example.com".to_string(),
        status: 0,
    });
}

#[test]
fn test_table_from_struct_vec() {
    #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
    struct Record {
        id: u64,
        name: String,
        value: i32,
    }

    let records = vec![
        Record { id: 1, name: "A".to_string(), value: 100 },
        Record { id: 2, name: "B".to_string(), value: 200 },
        Record { id: 3, name: "C".to_string(), value: 300 },
    ];

    // Manually convert to Table
    let mut table = Table::new();
    table.columns = vec![
        Value::String("id".to_string()),
        Value::String("name".to_string()),
        Value::String("value".to_string()),
    ];
    for rec in &records {
        table.rows.push(vec![
            Value::U64(rec.id),
            Value::String(rec.name.clone()),
            Value::I32(rec.value),
        ]);
    }

    let value = Value::Table(table);
    let json = serde_json::to_string(&value).unwrap();

    // Verify JSON format
    assert!(json.contains("\"columns\":"));
    assert!(json.contains("\"rows\":"));
    assert!(json.contains("[1,\"A\",100]"));
}

#[test]
fn test_table_to_vec_struct() {
    #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
    struct User {
        id: u64,
        name: String,
    }

    let mut table = Table::new();
    table.columns = vec![Value::String("id".into()), Value::String("name".into())];
    table.rows = vec![
        vec![Value::U64(1), Value::String("Alice".into())],
        vec![Value::U64(2), Value::String("Bob".into())],
    ];

    let users: Vec<User> = table.to_vec().unwrap();

    assert_eq!(users.len(), 2);
    assert_eq!(users[0], User { id: 1, name: "Alice".to_string() });
    assert_eq!(users[1], User { id: 2, name: "Bob".to_string() });
}

#[test]
fn test_table_with_different_types() {
    #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
    struct Item {
        flag: bool,
        count: i32,
        name: String,
    }

    let mut table = Table::new();
    table.columns = vec![
        Value::String("flag".into()),
        Value::String("count".into()),
        Value::String("name".into()),
    ];
    table.rows = vec![
        vec![Value::Bool(true), Value::I32(10), Value::String("item1".into())],
        vec![Value::Bool(false), Value::I32(20), Value::String("item2".into())],
    ];

    let items: Vec<Item> = table.to_vec().unwrap();

    assert_eq!(items[0].flag, true);
    assert_eq!(items[0].count, 10);
    assert_eq!(items[0].name, "item1");
    assert_eq!(items[1].flag, false);
}

#[test]
fn test_table_empty_columns_with_rows() {
    // Edge case: columns empty but rows have data
    let mut table = Table::new();
    // columns is empty
    table.add_row(vec![Value::I32(1), Value::String("Alice".to_string())]);

    let value = Value::Table(table);
    let json = serde_json::to_string(&value).unwrap();
    let recovered: Value = serde_json::from_str(&json).unwrap();
    let t = recovered.as_table().unwrap();

    assert_eq!(t.columns.len(), 0);
    assert_eq!(t.rows.len(), 1);
    assert_eq!(t.rows[0].len(), 2);
}

#[test]
fn test_table_zero_rows() {
    // Edge case: has columns but no rows
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    // no add_row

    let value = Value::Table(table.clone());
    assert!(value.is_empty());
    assert_eq!(value.len(), 0);
    assert_eq!(table.row_count(), 0);
    assert_eq!(table.column_count(), 2);

    let json = serde_json::to_string(&value).unwrap();
    let recovered: Value = serde_json::from_str(&json).unwrap();
    let t = recovered.as_table().unwrap();
    assert_eq!(t.columns.len(), 2);
    assert_eq!(t.rows.len(), 0);
}

#[test]
fn test_table_row_length_mismatch() {
    // Edge case: row field count doesn't match columns
    #[derive(Debug, PartialEq, serde::Deserialize)]
    struct Pair {
        a: i32,
        b: String,
    }

    let mut table = Table::new();
    table.add_column(Value::String("a".to_string()));
    table.add_column(Value::String("b".to_string()));
    table.add_row(vec![Value::I32(1)]); // only 1 field, but columns has 2

    // Test via from_value path
    let value = Value::Table(table);
    let result: Result<Vec<Pair>, _> = rbs::from_value(value);
    // Insufficient fields should fail
    assert!(result.is_err());
}

#[test]
fn test_table_from_value_empty_columns_with_rows() {
    // from_value path: columns empty but rows have data
    let mut table = Table::new();
    table.add_row(vec![Value::I32(1), Value::String("Alice".to_string())]);

    let value = Value::Table(table);
    let vec: Vec<serde_json::Value> = rbs::from_value(value).unwrap();
    assert_eq!(vec.len(), 1);
    assert_eq!(vec[0].as_array().unwrap().len(), 2);
}

#[test]
fn test_table_from_value_zero_rows() {
    // from_value path: has columns but no rows
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));

    let value = Value::Table(table);
    let vec: Vec<serde_json::Value> = rbs::from_value(value).unwrap();
    assert_eq!(vec.len(), 0);
}

#[test]
fn test_table_from_value_with_nulls() {
    // from_value path: middle field is null, mapped to Option
    #[derive(Debug, PartialEq, serde::Deserialize)]
    struct Record {
        a: String,
        b: Option<String>,
        c: String,
    }

    let mut table = Table::new();
    table.add_column(Value::String("a".to_string()));
    table.add_column(Value::String("b".to_string()));
    table.add_column(Value::String("c".to_string()));
    table.add_row(vec![
        Value::String("first".to_string()),
        Value::Null,
        Value::String("third".to_string()),
    ]);

    let value = Value::Table(table);
    let records: Vec<Record> = rbs::from_value(value).unwrap();
    assert_eq!(records[0].a, "first");
    assert_eq!(records[0].b, None);
    assert_eq!(records[0].c, "third");
}

#[test]
fn test_table_from_value_too_many_fields() {
    // Edge case: row has more fields than columns
    #[derive(Debug, PartialEq, serde::Deserialize)]
    struct Triple {
        a: i32,
        b: String,
    }

    let mut table = Table::new();
    table.add_column(Value::String("a".to_string()));
    table.add_column(Value::String("b".to_string()));
    table.add_row(vec![Value::I32(1), Value::String("x".to_string()), Value::I32(99)]); // 3 fields

    let value = Value::Table(table);
    let result: Result<Vec<Triple>, _> = rbs::from_value(value);
    // Extra fields are ignored by serde by default, so should succeed
    let vec = result.unwrap();
    assert_eq!(vec.len(), 1);
    assert_eq!(vec[0].a, 1);
    assert_eq!(vec[0].b, "x");
}

#[test]
fn test_table_deserialize_invalid_json_no_columns() {
    // Edge case: JSON missing columns field
    let json = r#"{"rows":[[1,"Alice"]]}"#;
    let value: Value = serde_json::from_str(json).unwrap();
    // Should fall back to regular Map, not Table
    assert!(value.is_map());
    assert!(!value.is_table());
}

#[test]
fn test_table_deserialize_invalid_json_no_rows() {
    // Edge case: JSON missing rows field
    let json = r#"{"columns":["id","name"]}"#;
    let value: Value = serde_json::from_str(json).unwrap();
    // Should fall back to regular Map
    assert!(value.is_map());
    assert!(!value.is_table());
}

#[test]
fn test_table_deserialize_invalid_json_extra_fields() {
    // Edge case: JSON has extra fields
    let json = r#"{"columns":["id"],"rows":[[1]],"extra":"value"}"#;
    let value: Value = serde_json::from_str(json).unwrap();
    // Should fall back to regular Map because of extra fields
    assert!(value.is_map());
    let map = value.as_map().unwrap();
    assert_eq!(map.len(), 3);
}

#[test]
fn test_table_to_vec_empty() {
    // to_vec on empty table should return empty Vec
    let table = Table::new();
    let result: Vec<serde_json::Value> = table.to_vec().unwrap();
    assert_eq!(result.len(), 0);
}

#[test]
fn test_table_from_value_empty_table() {
    // from_value path: completely empty table
    let table = Table::new();
    let value = Value::Table(table);
    let vec: Vec<serde_json::Value> = rbs::from_value(value).unwrap();
    assert_eq!(vec.len(), 0);
}

#[test]
fn test_table_to_maps() {
    // Test conversion from Table to Vec<ValueMap>
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_column(Value::String("age".to_string()));
    table.add_row(vec![
        Value::I32(1),
        Value::String("Alice".to_string()),
        Value::I32(25),
    ]);
    table.add_row(vec![
        Value::I32(2),
        Value::String("Bob".to_string()),
        Value::I32(30),
    ]);

    let maps = table.to_maps();
    assert_eq!(maps.len(), 2);

    // Verify first row map
    let map0 = &maps[0];
    assert_eq!(map0.get(&Value::String("id".to_string())), &Value::I32(1));
    assert_eq!(map0.get(&Value::String("name".to_string())), &Value::String("Alice".to_string()));
    assert_eq!(map0.get(&Value::String("age".to_string())), &Value::I32(25));

    // Verify second row map
    let map1 = &maps[1];
    assert_eq!(map1.get(&Value::String("id".to_string())), &Value::I32(2));
    assert_eq!(map1.get(&Value::String("name".to_string())), &Value::String("Bob".to_string()));
    assert_eq!(map1.get(&Value::String("age".to_string())), &Value::I32(30));
}

#[test]
fn test_table_to_maps_empty() {
    // Empty table should produce empty Vec
    let table = Table::new();
    let maps = table.to_maps();
    assert_eq!(maps.len(), 0);
}

#[test]
fn test_table_to_maps_with_null() {
    // Test that null values are correctly mapped
    let mut table = Table::new();
    table.add_column(Value::String("a".to_string()));
    table.add_column(Value::String("b".to_string()));
    table.add_row(vec![Value::String("x".to_string()), Value::Null]);

    let maps = table.to_maps();
    assert_eq!(maps[0].get(&Value::String("a".to_string())), &Value::String("x".to_string()));
    assert_eq!(maps[0].get(&Value::String("b".to_string())), &Value::Null);
}

#[test]
fn test_table_from_maps() {
    // Test conversion from Vec<ValueMap> to Table
    let mut m1 = ValueMap::new();
    m1.insert(Value::String("id".to_string()), Value::I32(1));
    m1.insert(Value::String("name".to_string()), Value::String("Alice".to_string()));

    let mut m2 = ValueMap::new();
    m2.insert(Value::String("id".to_string()), Value::I32(2));
    m2.insert(Value::String("name".to_string()), Value::String("Bob".to_string()));

    let table = Table::from_maps(&[m1, m2]);

    assert_eq!(table.column_count(), 2);
    assert_eq!(table.row_count(), 2);
    assert_eq!(table.columns[0], Value::String("id".to_string()));
    assert_eq!(table.columns[1], Value::String("name".to_string()));
    assert_eq!(table.get(0, 0), Some(&Value::I32(1)));
    assert_eq!(table.get(0, 1), Some(&Value::String("Alice".to_string())));
    assert_eq!(table.get(1, 0), Some(&Value::I32(2)));
    assert_eq!(table.get(1, 1), Some(&Value::String("Bob".to_string())));
}

#[test]
fn test_table_from_maps_empty() {
    // Empty input should produce empty table
    let table = Table::from_maps(&[]);
    assert_eq!(table.column_count(), 0);
    assert_eq!(table.row_count(), 0);
}

#[test]
fn test_table_from_maps_with_missing_keys() {
    // If a map is missing a key that exists in another map, fill with Null
    let mut m1 = ValueMap::new();
    m1.insert(Value::String("a".to_string()), Value::I32(1));
    m1.insert(Value::String("b".to_string()), Value::I32(2));

    let mut m2 = ValueMap::new();
    m2.insert(Value::String("a".to_string()), Value::I32(3));
    // "b" is missing

    let table = Table::from_maps(&[m1, m2]);

    assert_eq!(table.column_count(), 2);
    assert_eq!(table.row_count(), 2);
    assert_eq!(table.get(0, 1), Some(&Value::I32(2))); // first row has b=2
    assert_eq!(table.get(1, 1), Some(&Value::Null));    // second row b is null
}

#[test]
fn test_table_from_maps_roundtrip() {
    // Table -> maps -> Table should produce equivalent structure
    let mut original = Table::new();
    original.add_column(Value::String("x".to_string()));
    original.add_column(Value::String("y".to_string()));
    original.add_row(vec![Value::I32(10), Value::String("ten".to_string())]);
    original.add_row(vec![Value::I32(20), Value::String("twenty".to_string())]);

    let maps = original.to_maps();
    let recovered = Table::from_maps(&maps);

    assert_eq!(original.columns, recovered.columns);
    assert_eq!(original.rows, recovered.rows);
}
