#[cfg(test)]
mod test {
    use crate::{DiagnosticCode, VirtualWorkspace};

    #[test]
    fn test_constructor() {
        let mut ws = VirtualWorkspace::new();

        ws.def_files(vec![
            (
                "init.lua",
                r#"
                A = meta("A")
                "#,
            ),
            (
                "meta.lua",
                r#"
            ---@class Attribute
            ---@class constructor: Attribute
            ---@overload fun(name: string, root_class?: string, strip_self?: boolean, return_mode?: "self"|"doc"|"default")

            ---@generic T
            ---@[constructor("__init")]
            ---@param name `T`
            ---@return T
            function meta(name)
            end
                "#,
            ),
        ]);

        let ty = ws.expr_ty("A");
        assert_eq!(ws.humanize_type(ty), "A");
    }

    #[test]
    fn test_def_attribute() {
        let mut ws = VirtualWorkspace::new_with_init_std_lib();

        ws.has_no_diagnostic(
            DiagnosticCode::AssignTypeMismatch,
            r#"
        ---@[lsp_optimization("skip_table_fields_check")]
        local config = {}
        "#,
        );
    }

    #[test]
    fn test_attribute_overload_uses_arg_type_for_diagnostic() {
        let mut ws = VirtualWorkspace::new();

        assert!(ws.has_no_diagnostic(
            DiagnosticCode::AttributeParamTypeMismatch,
            r#"
        ---@class Attribute
        ---@class custom_attribute: Attribute
        ---@overload fun(value: string)
        ---@overload fun(value: integer)

        ---@[custom_attribute(1)]
        local value
        "#,
        ));
    }

    #[test]
    fn test_delayed_definition() {
        let mut ws = VirtualWorkspace::new_with_init_std_lib();

        ws.def(
            r#"
        ---@[lsp_optimization("delayed_definition")]
        local config

        function func()
            A = config
        end

        config = 1
        "#,
        );

        let ty = ws.expr_ty("A");
        let ty_desc = ws.humanize_type(ty);
        assert_eq!(ty_desc, "integer");
    }

    #[test]
    fn test_constructor_attribute() {
        let mut ws = VirtualWorkspace::new();

        ws.def_files(vec![
            (
                "1_main.lua",
                r#"
                local MyClass = require("2_myclass")

                instance = MyClass("Test")
                "#,
            ),
            (
                "2_myclass.lua",
                r#"
                ---@class MyClass
                local MyClass = meta("MyClass")

                ---@param name string
                function MyClass:init(name)
                end

                return MyClass
                "#,
            ),
            (
                "3_meta.lua",
                r#"
                ---@class Attribute
                ---@class constructor: Attribute
                ---@overload fun(name: string, root_class?: string, strip_self?: boolean, return_mode?: "self"|"doc"|"default")

                ---@class class
                ---@field is_class true

                ---@generic T
                ---@[constructor("init", "class")]
                ---@param class `T`
                ---@return T
                function meta(class) return {} end
                "#,
            ),
        ]);

        let ty = ws.expr_ty("instance");
        let ty_desc = ws.humanize_type(ty);
        assert_eq!(ty_desc, "MyClass");
    }

    #[test]
    fn test_issue_1008() {
        let mut ws = VirtualWorkspace::new();
        ws.def_file(
            "init.lua",
            r#"
            ---@class Attribute
            ---@class constructor: Attribute
            ---@overload fun(name: string, root_class?: string, strip_self?: boolean, return_mode?: "self"|"doc"|"default")

            ---@generic T
            ---@[constructor("init")]
            ---@param class `T`
            ---@return T
            function class(class) return {} end

            ---@class ClassB<T>
            ClassB = class("ClassB")

            ---@param value T
            function ClassB:init(value) end
            "#,
        );

        ws.def(
            r#"
            A = ClassB("I'm ClassB")
            "#,
        );

        let ty = ws.expr_ty("A");
        let ty_desc = ws.humanize_type(ty);
        assert_eq!(ty_desc, "ClassB<string>");
    }

    #[test]
    fn test_issue_1008_new_mode() {
        let mut ws = VirtualWorkspace::new();
        ws.def_file(
            "init.lua",
            r#"
            ---@class Attribute
            ---@class constructor: Attribute
            ---@overload fun(name: string, root_class?: string, strip_self?: boolean, return_mode?: "self"|"doc"|"default")

            ---@generic T
            ---@[constructor("init")]
            ---@param class `T`
            ---@return T
            function class(class) return {} end

            ---@class ClassB<T>
            ClassB = class("ClassB")

            ---@generic T
            ---@param value T
            ---@return ClassB<T>
            function ClassB:init(value) end
            "#,
        );

        ws.def(
            r#"
            A = ClassB("I'm ClassB")
            "#,
        );

        let ty = ws.expr_ty("A");
        let ty_desc = ws.humanize_type(ty);
        assert_eq!(ty_desc, "ClassB<string>");
    }

    #[test]
    fn test_attribute_constructor_return_mode() {
        let mut ws = VirtualWorkspace::new();
        ws.def_file(
            "init.lua",
            r#"
                ---@class Attribute
                ---@class constructor: Attribute
                ---@overload fun(name: string, root_class?: string, strip_self?: boolean, return_mode?: "self"|"doc"|"default")

                ---@generic T
                ---@[constructor("__init")]
                ---@param name `T`
                ---@return T
                function class(name)
                    return {}
                end
            "#,
        );

        ws.def(
            r#"
            ---@class ClassA
            ---@field a number
            local classA = class("ClassA")

            function classA:__init()
                self.a = 1
            end
            A = classA()
            "#,
        );

        let ty = ws.expr_ty("A");
        let ty_desc = ws.humanize_type(ty);
        assert_eq!(ty_desc, "ClassA");
    }
}
