import unittest
from pathlib import Path
from tests.dotnet_code_helper import *
from unicontract.emitters.DotnetEmmiter import *


class TestEmitterDotnetDefault(unittest.TestCase):

    def setUpClass():
        dotnet_code_helper.init_roslyn()

    def test_empty_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText(""""""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 0)

    def test_empty_namespace_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace{
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 0)

    def test_create_folder_structure_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace.SubNameSpace{
    interface Empty{
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter(configuration={"dotnet.create_folder_structure": True})
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        self.assertEqual(Path(result[0].fullPath), Path("./SomeNamespace/SubNameSpace/Empty.cs"))

        emitter = DotnetEmmiter(configuration={"dotnet.create_folder_structure": False})
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        self.assertEqual(Path(result[0].fullPath), Path("./Empty.cs"))

    def test_can_build_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace{
    enum SomeEnum{
        Value1,
        Value2
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)

        compiled, errors, assembly = dotnet_code_helper.compile_debug(result, dotnet_code_helper.assembly_name())
        self.assertTrue(compiled)

    def test_enum_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace{
    enum SomeEnum{
        Value1,
        Value2
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        expected = """
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace SomeNamespace
{
    enum SomeEnum
    {
        Value1,
        Value2,
    }
}
"""
        self.assertEqual(result[0].fileName, "SomeEnum.cs")
        equal, index, diff_part_1, diff_part_2 = dotnet_code_helper.compare_and_extract_diff(expected, result[0].content)
        self.assertTrue(equal)
        compiled, errors, assembly = dotnet_code_helper.compile_debug(result, dotnet_code_helper.assembly_name())
        self.assertTrue(compiled)

    def test_enum_document_lines_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace{
    # doc line 1 for enum
    # doc line 2 for enum
    enum SomeEnum{
        # doc line 1 for enum value 1
        # doc line 2 for enum value 1
        Value1,
        # doc line 1 for enum value 2
        # doc line 2 for enum value 2
        Value2
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        expected = """
// <auto-generated>
//     This code was generated by unicontract
//     see more information: https://github.com/gyorgy-gulyas/UniContract
//
//     Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>

using System;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace SomeNamespace
{
    /// doc line 1 for enum
    /// doc line 2 for enum
    enum SomeEnum
    {
        /// doc line 1 for enum value 1
        /// doc line 2 for enum value 1
        Value1,
        /// doc line 1 for enum value 2
        /// doc line 2 for enum value 2
        Value2,
    }

}"""
        self.assertEqual(result[0].fileName, "SomeEnum.cs")
        equal, index, diff_part_1, diff_part_2 = dotnet_code_helper.compare_and_extract_diff(expected, result[0].content, with_comments=True)
        self.assertTrue(equal)
        compiled, errors, assembly = dotnet_code_helper.compile_debug(result, dotnet_code_helper.assembly_name())
        self.assertTrue(compiled)

    def test_interface_empty_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace{
    interface SomeInterface{
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        expected = """
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace SomeNamespace
{
    interface SomeInterface
    {
    }
}
"""
        self.assertEqual(result[0].fileName, "SomeInterface.cs")
        equal, index, diff_part_1, diff_part_2 = dotnet_code_helper.compare_and_extract_diff(expected, result[0].content)
        self.assertTrue(equal)
        compiled, errors, assembly = dotnet_code_helper.compile_debug(result, dotnet_code_helper.assembly_name())
        self.assertTrue(compiled)

    def test_interface_properties_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace{
    interface SomeInterface{
        property name: string
        property value: integer
        readonly property calculated: integer
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        expected = """
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace SomeNamespace
{

    interface SomeInterface
    {
        public string name { get; set; }
        public int value { get; set; }
        public int calculated { get; }
    }

}
"""
        self.assertEqual(result[0].fileName, "SomeInterface.cs")
        equal, index, diff_part_1, diff_part_2 = dotnet_code_helper.compare_and_extract_diff(expected, result[0].content)
        self.assertTrue(equal)
        compiled, errors, assembly = dotnet_code_helper.compile_debug(result, dotnet_code_helper.assembly_name())
        self.assertTrue(compiled)

    def test_interface_methods_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace{
    interface SomeInterface{
        method Create( id:string, type:number ) => integer
        async method CreateAsync( id:string, now:date ) => integer
        method Done()
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        expected = """
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace SomeNamespace
{

    interface SomeInterface
    {
        int Create(string id, decimal type);
        Task<int> CreateAsync(string id, DateOnly now);
        void Done();
    }

}
"""
        self.assertEqual(result[0].fileName, "SomeInterface.cs")
        equal, index, diff_part_1, diff_part_2 = dotnet_code_helper.compare_and_extract_diff(expected, result[0].content)
        self.assertTrue(equal)
        compiled, errors, assembly = dotnet_code_helper.compile_debug(result, dotnet_code_helper.assembly_name())
        self.assertTrue(compiled)

    def test_interface_document_lines_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
namespace SomeNamespace{
    # this interface will help you to createm modify and delete Partners
    interface Partner {
        enum Type {
            PrivatePerson,
            Company    
        }
        #name of the partner
        property name: string
        #type of the partner
        property type: Type

        #method without param 
        method Create() => integer
        
        #async method without param 
        async method CreateAsync() => integer
                                                
        #async method without param and without return value
        async method CreateAsync2()

        #method with one param
        method DeletePartnerNormal( partnerId:integer ) => boolean

        #method with two param
        method DeletePartnerMultiple2( partnerId1:integer, partnerId2:integer ) => boolean

        #method with 5 param
        method DeletePartnerMultiple5( partnerId1:integer, partnerId2:integer, partnerId3:integer, partnerId4:integer, partnerId5:integer ) => boolean

        #method with documented parameters 
        method DeletePartnerForce( 
            #deleted partner id
            partnerId:integer,
            #delete when has a reference
            force:boolean ) => boolean
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        expected = """
// <auto-generated>
//     This code was generated by unicontract
//     see more information: https://github.com/gyorgy-gulyas/UniContract
//
//     Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>

using System;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace SomeNamespace
{

    /// this interface will help you to createm modify and delete Partners
    interface Partner
    {

        enum Type
        {
            PrivatePerson,
            Company,
        }

        ///name of the partner
        public string name { get; set;}
        ///type of the partner
        public Type type { get; set;}

        ///method without param 
        int Create();
        ///async method without param 
        Task<int> CreateAsync();
        ///async method without param and without return value
        Task CreateAsync2();
        ///method with one param
        bool DeletePartnerNormal(int partnerId);
        ///method with two param
        bool DeletePartnerMultiple2(int partnerId1, int partnerId2);
        ///method with 5 param
        bool DeletePartnerMultiple5(
            int partnerId1, 
            int partnerId2, 
            int partnerId3, 
            int partnerId4, 
            int partnerId5);
        ///method with documented parameters 
        bool DeletePartnerForce(
            ///deleted partner id
            int partnerId, 
            ///delete when has a reference
            bool force);
    }

}"""
        self.assertEqual(result[0].fileName, "Partner.cs")
        equal, index, diff_part_1, diff_part_2 = dotnet_code_helper.compare_and_extract_diff(expected, result[0].content,with_comments=True)
        self.assertTrue(equal)
        compiled, errors, assembly = dotnet_code_helper.compile_debug(result, dotnet_code_helper.assembly_name())
        self.assertTrue(compiled)

    def test_imports_ok(self):
        engine = Engine()
        session = Session(Source.CreateFromText("""
#file handling namespace
import "System.IO"
                                               
namespace SomeNamespace{
    interface Empty{
    }
}"""))
        root = engine.Build(session)
        self.assertFalse(session.HasAnyError())

        emitter = DotnetEmmiter()
        result = emitter.Emit(session)
        self.assertEqual(len(result), 1)
        expected = """
// <auto-generated>
//     This code was generated by unicontract
//     see more information: https://github.com/gyorgy-gulyas/UniContract
//
//     Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
///file handling namespace
using System.IO;

namespace SomeNamespace
{
    interface Empty
    {
    }
}"""
        self.assertEqual(result[0].fileName, "Empty.cs")
        equal, index, diff_part_1, diff_part_2 = dotnet_code_helper.compare_and_extract_diff(expected, result[0].content,with_comments=True)
        self.assertTrue(equal)
        compiled, errors, assembly = dotnet_code_helper.compile_debug(result, dotnet_code_helper.assembly_name())
        self.assertTrue(compiled)


if __name__ == "__main__":
    unittest.main()
