// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Tests for the `#[fory(generate_default)]` attribute.
//!
//! By default, `ForyObject` does NOT generate `impl Default` to avoid conflicts
//! with existing `Default` implementations. Use `#[fory(generate_default)]` when
//! you want the macro to generate both `ForyDefault` and `Default` for you.

use fory_core::fory::Fory;
use fory_derive::ForyObject;

/// Test struct with manual Default implementation.
/// ForyObject does NOT generate Default by default, so there's no conflict.
#[derive(ForyObject, Debug, PartialEq)]
struct NodeWithCustomDefault {
    addr: String,
}

impl Default for NodeWithCustomDefault {
    fn default() -> Self {
        Self {
            addr: "localhost:8080".to_string(),
        }
    }
}

#[test]
fn test_no_default_conflict() {
    let mut fory = Fory::default();
    fory.register::<NodeWithCustomDefault>(1).unwrap();

    let node = NodeWithCustomDefault {
        addr: "192.168.1.1:3000".to_string(),
    };

    let bytes = fory.serialize(&node).unwrap();
    let decoded: NodeWithCustomDefault = fory.deserialize(&bytes).unwrap();
    assert_eq!(node, decoded);
}

#[test]
fn test_custom_default_is_preserved() {
    // Verify that our custom Default implementation is used
    let node = NodeWithCustomDefault::default();
    assert_eq!(node.addr, "localhost:8080");
}

/// Test enum with manual Default implementation.
/// ForyObject does NOT generate Default by default.
#[derive(ForyObject, Debug, PartialEq)]
enum StatusWithCustomDefault {
    Active,
    Inactive,
    Pending,
}

// Allow derivable_impls because we're specifically testing manual Default impl
#[allow(clippy::derivable_impls)]
impl Default for StatusWithCustomDefault {
    fn default() -> Self {
        // Custom default: Pending instead of Active (first variant)
        StatusWithCustomDefault::Pending
    }
}

#[test]
fn test_enum_no_default_conflict() {
    let mut fory = Fory::default();
    fory.register::<StatusWithCustomDefault>(2).unwrap();

    let status = StatusWithCustomDefault::Active;
    let bytes = fory.serialize(&status).unwrap();
    let decoded: StatusWithCustomDefault = fory.deserialize(&bytes).unwrap();
    assert_eq!(status, decoded);
}

#[test]
fn test_enum_custom_default_is_preserved() {
    // Verify that our custom Default implementation is used
    let status = StatusWithCustomDefault::default();
    assert_eq!(status, StatusWithCustomDefault::Pending);
}

/// Test that generate_default = true generates Default impl
#[derive(ForyObject, Debug, PartialEq)]
#[fory(generate_default)]
struct StructWithGeneratedDefault {
    value: i32,
}

#[test]
fn test_generate_default_struct() {
    let mut fory = Fory::default();
    fory.register::<StructWithGeneratedDefault>(3).unwrap();

    let data = StructWithGeneratedDefault { value: 42 };
    let bytes = fory.serialize(&data).unwrap();
    let decoded: StructWithGeneratedDefault = fory.deserialize(&bytes).unwrap();
    assert_eq!(data, decoded);

    // Default should work (generated by ForyObject)
    let default_data = StructWithGeneratedDefault::default();
    assert_eq!(default_data.value, 0);
}

/// Test that generate_default = true generates Default impl for enums
#[derive(ForyObject, Debug, PartialEq)]
#[fory(generate_default)]
enum EnumWithGeneratedDefault {
    First,
    Second,
    Third,
}

#[test]
fn test_generate_default_enum() {
    let mut fory = Fory::default();
    fory.register::<EnumWithGeneratedDefault>(4).unwrap();

    let data = EnumWithGeneratedDefault::Second;
    let bytes = fory.serialize(&data).unwrap();
    let decoded: EnumWithGeneratedDefault = fory.deserialize(&bytes).unwrap();
    assert_eq!(data, decoded);

    // Default should be First variant (generated by ForyObject)
    let default_data = EnumWithGeneratedDefault::default();
    assert_eq!(default_data, EnumWithGeneratedDefault::First);
}

/// Test that generate_default = false works (same as not specifying)
#[derive(ForyObject, Debug, PartialEq)]
#[fory(generate_default = false)]
struct StructWithoutGeneratedDefault {
    value: i32,
}

impl Default for StructWithoutGeneratedDefault {
    fn default() -> Self {
        Self { value: 100 }
    }
}

#[test]
fn test_generate_default_false() {
    let mut fory = Fory::default();
    fory.register::<StructWithoutGeneratedDefault>(5).unwrap();

    let data = StructWithoutGeneratedDefault { value: 42 };
    let bytes = fory.serialize(&data).unwrap();
    let decoded: StructWithoutGeneratedDefault = fory.deserialize(&bytes).unwrap();
    assert_eq!(data, decoded);

    // Custom Default should be used
    let default_data = StructWithoutGeneratedDefault::default();
    assert_eq!(default_data.value, 100);
}
