//! Tests for the i18n! proc macro.

// === Basic template entries ===

mod basic {
    mad_hatter_macros::i18n! {
        hello() = "Hello, world!";
        greeting(name: &str) = "Hello, {name}!";
        multi_arg(a: &str, b: u32) = "Item {a} count {b}";
        format_spec(err: &dyn std::fmt::Display) = "Error: {err:#}";
        no_placeholder() = "Static message";
        empty_args_with_placeholder(x: u64) = "{x} items";
    }

    #[test]
    fn test_no_args() {
        assert_eq!(hello().unseal(), "Hello, world!");
    }

    #[test]
    fn test_single_arg() {
        assert_eq!(greeting("Alice").unseal(), "Hello, Alice!");
    }

    #[test]
    fn test_multi_args() {
        assert_eq!(multi_arg("widget", 42).unseal(), "Item widget count 42");
    }

    #[test]
    fn test_format_spec() {
        let err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
        let sealed = format_spec(&err);
        let result = sealed.unseal();
        assert!(result.contains("file missing"), "got: {result}");
    }

    #[test]
    fn test_no_placeholder() {
        assert_eq!(no_placeholder().unseal(), "Static message");
    }

    #[test]
    fn test_numeric_arg() {
        assert_eq!(empty_args_with_placeholder(99).unseal(), "99 items");
    }
}

// === Block entries ===

mod block_entries {
    mad_hatter_macros::i18n! {
        simple_block(x: u32) = {
            format!("value is {}", x)
        };

        conditional(flag: bool) = {
            if flag {
                "enabled".to_string()
            } else {
                "disabled".to_string()
            }
        };

        complex_logic(items: &[&str]) = {
            items.join(", ")
        };

        with_unseal(sealed: &mad_hatter::Sealed<String>) = {
            format!("wrapped: {}", sealed)
        };
    }

    #[test]
    fn test_simple_block() {
        assert_eq!(simple_block(42).unseal(), "value is 42");
    }

    #[test]
    fn test_conditional_true() {
        assert_eq!(conditional(true).unseal(), "enabled");
    }

    #[test]
    fn test_conditional_false() {
        assert_eq!(conditional(false).unseal(), "disabled");
    }

    #[test]
    fn test_complex_logic() {
        assert_eq!(complex_logic(&["a", "b", "c"]).unseal(), "a, b, c");
    }

    #[test]
    fn test_with_unseal() {
        let s = mad_hatter::Sealed::new("inner".to_string());
        assert_eq!(with_unseal(&s).unseal(), "wrapped: inner");
    }
}

// === Mixed entries (template + block in same invocation) ===

mod mixed {
    mad_hatter_macros::i18n! {
        template_entry(name: &str) = "Hello {name}";
        block_entry(n: usize) = {
            if n == 0 { "primary".to_string() } else { format!("extra{}", n) }
        };
        another_template(a: u32, b: u32) = "from {a} to {b}";
    }

    #[test]
    fn test_mixed_template() {
        assert_eq!(template_entry("world").unseal(), "Hello world");
    }

    #[test]
    fn test_mixed_block() {
        assert_eq!(block_entry(0).unseal(), "primary");
        assert_eq!(block_entry(1).unseal(), "extra1");
    }

    #[test]
    fn test_mixed_another() {
        assert_eq!(another_template(1, 10).unseal(), "from 1 to 10");
    }
}

// === Return type is Sealed<String> ===

mod sealed_return {
    mad_hatter_macros::i18n! {
        check_sealed() = "sealed value";
    }

    #[test]
    fn test_returns_sealed() {
        let result: mad_hatter::Sealed<String> = check_sealed();
        assert_eq!(result.unseal(), "sealed value");
    }
}

// === Sealed auto-unseal ===

mod sealed_auto {
    mad_hatter_macros::i18n! {
        // Template syntax: Sealed param auto-unsealed in format!
        sealed_template(name: &str, tag: &mad_hatter::Sealed<String>) =
            "name={name} tag={tag}";

        // Block syntax: Sealed param auto-unsealed before block
        sealed_block(tag: &mad_hatter::Sealed<String>) = {
            format!("tag is {}", tag)
        };

        // Owned Sealed param
        sealed_owned(tag: mad_hatter::Sealed<String>) =
            "owned={tag}";

        // Mixed: some Sealed, some not
        sealed_mixed(label: &str, s1: &mad_hatter::Sealed<String>, count: u32, s2: &mad_hatter::Sealed<String>) =
            "{label}: {s1} ({count}) {s2}";
    }

    #[test]
    fn test_sealed_template() {
        let tag = mad_hatter::Sealed::new("v1".to_string());
        assert_eq!(sealed_template("foo", &tag).unseal(), "name=foo tag=v1");
    }

    #[test]
    fn test_sealed_block() {
        let tag = mad_hatter::Sealed::new("hello".to_string());
        assert_eq!(sealed_block(&tag).unseal(), "tag is hello");
    }

    #[test]
    fn test_sealed_owned() {
        let tag = mad_hatter::Sealed::new("owned_val".to_string());
        assert_eq!(sealed_owned(tag).unseal(), "owned=owned_val");
    }

    #[test]
    fn test_sealed_mixed() {
        let s1 = mad_hatter::Sealed::new("alpha".to_string());
        let s2 = mad_hatter::Sealed::new("beta".to_string());
        assert_eq!(
            sealed_mixed("test", &s1, 42, &s2).unseal(),
            "test: alpha (42) beta"
        );
    }
}

// === Edge cases ===

mod edge_cases {
    mad_hatter_macros::i18n! {
        escaped_braces() = "use {{braces}} in output";
        unicode_template(name: &str) = "你好 {name}！🎉";
        multiline_template(a: &str, b: &str) = "line1: {a}\nline2: {b}";
    }

    #[test]
    fn test_escaped_braces() {
        assert_eq!(escaped_braces().unseal(), "use {braces} in output");
    }

    #[test]
    fn test_unicode() {
        assert_eq!(unicode_template("世界").unseal(), "你好 世界！🎉");
    }

    #[test]
    fn test_multiline() {
        assert_eq!(
            multiline_template("foo", "bar").unseal(),
            "line1: foo\nline2: bar"
        );
    }
}

// === #[i18n_fn] attribute macro ===

mod i18n_fn_attr {
    use mad_hatter::Sealed;

    #[mad_hatter::i18n_fn]
    fn returns_sealed() -> Sealed<String> {
        Sealed::new("hello".to_string())
    }

    #[mad_hatter::i18n_fn]
    fn returns_option_sealed(flag: bool) -> Option<Sealed<String>> {
        if flag { Some(Sealed::new("yes".to_string())) } else { None }
    }

    #[mad_hatter::i18n_fn]
    fn returns_result_sealed() -> Result<Sealed<String>, String> {
        Ok(Sealed::new("ok".to_string()))
    }

    #[test]
    fn test_i18n_fn_sealed() {
        assert_eq!(returns_sealed().unseal(), "hello");
    }

    #[test]
    fn test_i18n_fn_option_sealed() {
        assert_eq!(returns_option_sealed(true).unwrap().unseal(), "yes");
        assert!(returns_option_sealed(false).is_none());
    }

    #[test]
    fn test_i18n_fn_result_sealed() {
        assert_eq!(returns_result_sealed().unwrap().unseal(), "ok");
    }
}

// === format_sealed returns Sealed ===

mod format_sealed_returns_sealed {
    use mad_hatter::{Sealed, format_sealed};

    #[test]
    fn test_format_sealed_returns_sealed_type() {
        let a = Sealed::new("world");
        let result = format_sealed("hello {}", &[&a]);
        // result is Sealed<String>, must unseal to access
        assert_eq!(result.unseal(), "hello world");
    }

    #[test]
    fn test_format_sealed_multiple_values() {
        let a = Sealed::new("foo");
        let b = Sealed::new("bar");
        let result = format_sealed("{} and {}", &[&a, &b]);
        assert_eq!(result.unseal(), "foo and bar");
    }
}