//! Comprehensive tests for cfgmatic derive macros.
//! Tests cover `Config` macro, `ConfigEnv` macro, validation, and error cases.

#![allow(dead_code)]

use cfgmatic::{Config, ConfigEnv, ConfigFields, ConfigLoader, EnvConfig};
use validator::Validate;

// ============================================================================
// Test: Basic Config derive
// ============================================================================

#[test]
fn test_config_basic() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "APP")]
    struct TestConfig {
        #[config(default = "8080")]
        port: u16,
        #[config(default = "localhost")]
        host: String,
    }

    let config = TestConfig::from_env().unwrap();
    assert_eq!(config.port, 8080);
    assert_eq!(config.host, "localhost");
    assert_eq!(TestConfig::env_prefix(), "APP");
}

// ============================================================================
// Test: Fields without #[config(...)] are skipped
// ============================================================================

#[test]
fn test_config_skip_fields_without_attr() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "SKIP")]
    struct SkipTest {
        #[config(default = "1")]
        enabled: u32,
        // These fields should be skipped
        complex_field: Vec<String>,
        nested_field: InnerStruct,
    }

    #[derive(Debug, Default)]
    struct InnerStruct {
        value: i32,
    }

    let config = SkipTest::from_env().unwrap();
    assert_eq!(config.enabled, 1);
    assert!(config.complex_field.is_empty());
    assert_eq!(config.nested_field.value, 0);
}

// ============================================================================
// Test: Custom env variable names
// ============================================================================

#[test]
fn test_config_custom_env_name() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "CUSTOM")]
    struct CustomEnvTest {
        #[config(env = "MY_CUSTOM_PORT", default = "3000")]
        port: u16,
    }

    let config = CustomEnvTest::from_env().unwrap();
    assert_eq!(config.port, 3000);
    assert_eq!(CustomEnvTest::env_prefix(), "CUSTOM");
}

// ============================================================================
// Test: Empty prefix
// ============================================================================

#[test]
fn test_config_empty_prefix() {
    #[derive(Debug, Default, Config)]
    struct EmptyPrefixTest {
        #[config(default = "42")]
        value: i32,
    }

    let config = EmptyPrefixTest::from_env().unwrap();
    assert_eq!(config.value, 42);
    assert_eq!(EmptyPrefixTest::env_prefix(), "");
}

// ============================================================================
// Test: ConfigEnv derive macro
// ============================================================================

#[test]
fn test_config_env_basic() {
    #[derive(ConfigEnv)]
    #[config_env(prefix = "ENV")]
    struct EnvTest {
        #[config_env(name = "PORT")]
        port: u16,
        #[config_env(name = "HOST")]
        host: String,
        // Field without attr should be skipped
        skipped: String,
    }

    let mappings = EnvTest::env_mappings();
    assert_eq!(mappings.len(), 2); // Only fields with #[config_env(...)]
    // When 'name' is specified, it's used as-is (full env var name)
    assert_eq!(mappings[0], ("port", "PORT"));
    assert_eq!(mappings[1], ("host", "HOST"));
    assert_eq!(EnvTest::env_prefix(), "ENV");
}

// ============================================================================
// Test: ConfigEnv without prefix
// ============================================================================

#[test]
fn test_config_env_no_prefix() {
    #[derive(ConfigEnv)]
    struct NoPrefixEnv {
        #[config_env(name = "VALUE")]
        value: u32,
    }

    let mappings = NoPrefixEnv::env_mappings();
    assert_eq!(mappings.len(), 1);
    assert_eq!(mappings[0], ("value", "VALUE"));
    assert_eq!(NoPrefixEnv::env_prefix(), "");
}

// ============================================================================
// Test: ConfigEnv auto-generated names
// ============================================================================

#[test]
fn test_config_env_auto_names() {
    #[derive(ConfigEnv)]
    #[config_env(prefix = "AUTO")]
    struct AutoNamesEnv {
        #[config_env]
        port: u16,
        #[config_env]
        database_url: String,
    }

    let mappings = AutoNamesEnv::env_mappings();
    assert_eq!(mappings.len(), 2);
    assert_eq!(mappings[0], ("port", "AUTO__PORT"));
    assert_eq!(mappings[1], ("database_url", "AUTO__DATABASE_URL"));
}

// ============================================================================
// Test: Validation using validator crate
// ============================================================================

#[test]
fn test_config_with_validator() {
    #[derive(Debug, Default, Config, Validate)]
    #[config(prefix = "VALIDATE")]
    struct ValidationTest {
        #[config(default = "8080")]
        #[validate(range(min = 1, max = 65535))]
        port: u16,
        #[config(default = "http://localhost")]
        #[validate(url)]
        url: String,
    }

    let config = ValidationTest::from_env().unwrap();
    assert_eq!(config.port, 8080);
    assert_eq!(config.url, "http://localhost");

    // Test validation - this should pass
    assert!(config.validate().is_ok());
}

// ============================================================================
// Test: Load from env with custom prefix
// ============================================================================

#[test]
fn test_load_from_env_custom_prefix() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "ORIGINAL")]
    struct CustomPrefixTest {
        #[config(default = "100")]
        value: u32,
    }

    let mut config = CustomPrefixTest::default();
    config.load_from_env(Some("CUSTOM")).unwrap();
    assert_eq!(config.value, 100);
}

// ============================================================================
// Test: Multiple structs with same prefix
// ============================================================================

#[test]
fn test_multiple_structs_same_prefix() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "SHARED")]
    struct FirstConfig {
        #[config(default = "1")]
        value1: u32,
    }

    #[derive(Debug, Default, Config)]
    #[config(prefix = "SHARED")]
    struct SecondConfig {
        #[config(default = "2")]
        value2: u32,
    }

    let config1 = FirstConfig::from_env().unwrap();
    let config2 = SecondConfig::from_env().unwrap();

    assert_eq!(config1.value1, 1);
    assert_eq!(config2.value2, 2);
    assert_eq!(FirstConfig::env_prefix(), SecondConfig::env_prefix());
}

// ============================================================================
// Test: Boolean parsing
// ============================================================================

#[test]
fn test_boolean_parsing() {
    #[derive(Debug, Default, Config)]
    struct BoolTest {
        #[config(default = "true")]
        enabled: bool,
        #[config(default = "false")]
        disabled: bool,
    }

    let config = BoolTest::from_env().unwrap();
    assert!(config.enabled);
    assert!(!config.disabled);
}

// ============================================================================
// Test: Various number types
// ============================================================================

#[test]
#[allow(clippy::approx_constant)]
fn test_various_number_types() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "NUMBERS")]
    #[allow(clippy::struct_field_names)]
    struct NumberTest {
        #[config(default = "127")]
        i8_val: i8,
        #[config(default = "255")]
        u8_val: u8,
        #[config(default = "-1000")]
        i16_val: i16,
        #[config(default = "1000")]
        u16_val: u16,
        #[config(default = "-100000")]
        i32_val: i32,
        #[config(default = "100000")]
        u32_val: u32,
        #[config(default = "-1000000")]
        i64_val: i64,
        #[config(default = "1000000")]
        u64_val: u64,
        #[config(default = "3.14")]
        f32_val: f32,
        #[config(default = "2.718281828")]
        f64_val: f64,
    }

    let config = NumberTest::from_env().unwrap();
    assert_eq!(config.i8_val, 127);
    assert_eq!(config.u8_val, 255);
    assert_eq!(config.i16_val, -1000);
    assert_eq!(config.u16_val, 1000);
    assert_eq!(config.i32_val, -100_000);
    assert_eq!(config.u32_val, 100_000);
    assert_eq!(config.i64_val, -1_000_000);
    assert_eq!(config.u64_val, 1_000_000);
    assert!((config.f32_val - 3.14).abs() < 0.001);
    assert!((config.f64_val - 2.718_281_828).abs() < 0.000_001);
}

// ============================================================================
// Test: No fields with #[config(...)] attribute
// ============================================================================

#[test]
fn test_no_config_fields() {
    #[derive(Debug, Default, Config)]
    struct NoConfigFields {
        value: u32,
        name: String,
    }

    let config = NoConfigFields::from_env().unwrap();
    assert_eq!(config.value, 0);
    assert_eq!(config.name, "");
}

// ============================================================================
// Test: Mixed fields (with and without config attr)
// ============================================================================

#[test]
fn test_mixed_fields() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "MIXED")]
    struct MixedFields {
        #[config(default = "42")]
        config_value: u32,
        regular_value: u32,
        #[config(default = "enabled")]
        config_string: String,
        regular_string: String,
    }

    let config = MixedFields::from_env().unwrap();
    assert_eq!(config.config_value, 42);
    assert_eq!(config.regular_value, 0);
    assert_eq!(config.config_string, "enabled");
    assert_eq!(config.regular_string, "");
}

// ============================================================================
// Test: ConfigFields trait methods
// ============================================================================

#[test]
fn test_config_fields_trait() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "TRAIT")]
    struct TraitTest {
        #[config(default = "99")]
        value: u32,
    }

    let mut config = TraitTest::default();

    // Test load_from_env with None prefix
    config.load_from_env(None).unwrap();
    assert_eq!(config.value, 99);

    // Test load_from_env with Some prefix
    config.value = 0;
    config.load_from_env(Some("OTHER")).unwrap();
    assert_eq!(config.value, 99);

    // Test env_prefix
    assert_eq!(TraitTest::env_prefix(), "TRAIT");
}

// ============================================================================
// Test: ConfigEnv with no fields having attribute
// ============================================================================

#[test]
fn test_config_env_no_fields() {
    #[derive(ConfigEnv)]
    #[config_env(prefix = "EMPTY")]
    struct EmptyEnv {
        value: u32,
        name: String,
    }

    let mappings = EmptyEnv::env_mappings();
    assert_eq!(mappings.len(), 0);
    assert_eq!(EmptyEnv::env_prefix(), "EMPTY");
}

// ============================================================================
// Test: Enum fields without FromStr are skipped
// ============================================================================

#[test]
fn test_enum_without_fromstr_skipped() {
    #[derive(Debug, Default, PartialEq)]
    enum SimpleEnum {
        #[default]
        A,
        B,
    }

    #[derive(Debug, Default, Config)]
    struct EnumTest {
        #[config(default = "1")]
        value: u32,
        // Enum without FromStr - should be skipped
        state: SimpleEnum,
    }

    let config = EnumTest::from_env().unwrap();
    assert_eq!(config.value, 1);
    assert_eq!(config.state, SimpleEnum::A);
}

// ============================================================================
// Test: Field with underscores in name
// ============================================================================

#[test]
fn test_field_with_underscores() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "UNDER")]
    struct UnderscoreTest {
        #[config(default = "123")]
        my_field_name: u32,
        #[config(default = "abc")]
        another_complex_name: String,
    }

    let config = UnderscoreTest::from_env().unwrap();
    assert_eq!(config.my_field_name, 123);
    assert_eq!(config.another_complex_name, "abc");

    // Verify env var name generation
    assert_eq!(UnderscoreTest::env_prefix(), "UNDER");
}

// ============================================================================
// Test: Multiple calls to load_from_env
// ============================================================================

#[test]
fn test_multiple_load_calls() {
    #[derive(Debug, Default, Config)]
    #[config(prefix = "MULTI")]
    struct MultiLoadTest {
        #[config(default = "10")]
        counter: u32,
    }

    let mut config = MultiLoadTest::default();

    // First load
    config.load_from_env(None).unwrap();
    assert_eq!(config.counter, 10);

    // Second load (should reapply default since no env var is set)
    config.load_from_env(None).unwrap();
    assert_eq!(config.counter, 10);
}

// ============================================================================
// Compile-fail tests (using cfg_attr to make them compile only for testing)
// ============================================================================

// Note: Actual compile-fail tests require separate test harness
// These are documented here for manual verification

// #[derive(Config)]  // Should fail: tuple struct
// struct TupleStruct(u32, String);

// #[derive(Config)]  // Should fail: unit struct
// struct UnitStruct;

// #[derive(ConfigEnv)]  // Should fail: tuple struct
// struct TupleEnv(u32);

// #[derive(ConfigEnv)]  // Should fail: unit struct
// struct UnitEnv;
