using FlutterCodeGenerator.Helper;
using FlutterCodeGenerator.Map.Interface;
using FlutterCodeGenerator.Model;
using FlutterCodeGenerator.ModelTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;

namespace FlutterCodeGenerator.Map
{
    internal class NotificationServiceMap : IServiceMap
    {
        private List<Type> _typeList;

        private EnumModelType _notificationEnumModelType;
        private List<ModelType> _notificationModelTypeList;

        public string ServiceName { get; private set; }
        public List<ComplexModelType> UsedComplexModelTypeList { get; }

        public NotificationServiceMap(Type notificationEnumType, List<Type> types)
        {
            ServiceName = "NotificationService";
            _typeList = types;
            UsedComplexModelTypeList = new List<ComplexModelType>();
            GenerateDataCache.Instance.SetCurrentServiceMap(this);
            _notificationModelTypeList = new List<ModelType>();
            _notificationEnumModelType = (EnumModelType)ModelTypeGenerator.Create(notificationEnumType, notificationEnumType.Name, true);
            foreach (var type in _typeList)
            {
                try
                {
                    _notificationModelTypeList.Add(ModelTypeGenerator.Create(type, type.Name, true));
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Type is {type.FullName},Error:{ex}");
                    throw;
                }
            }
        }

        public string GetServiceDartString()
        {
            return null;
        }

        public string GetServiceModelDartString()
        {
            var dartString = new StringBuilder();
            var importServiceList = new List<string>();
            var alreadyGeneratedList = GenerateDataCache.Instance.AlreadyGeneratedList;
            foreach (var modelType in UsedComplexModelTypeList)
            {
                if (!alreadyGeneratedList.Any(x => x.Key.ParameterType.Name == modelType.ParameterType.Name && x.Key.ParameterType.Namespace == modelType.ParameterType.Namespace))
                {
                    alreadyGeneratedList.Add(modelType, ServiceName);
                    dartString.AppendLine(modelType.GetDartString());
                }
                else
                {
                    var importService = alreadyGeneratedList.FirstOrDefault(x => x.Key.ParameterType.Name == modelType.ParameterType.Name && x.Key.ParameterType.Namespace == modelType.ParameterType.Namespace).Value;
                    if (importService == null)
                    {
                        throw new Exception("Import Service is null");
                    }
                    if (importService != ServiceName && !importServiceList.Contains(importService))
                    {
                        importServiceList.Add(importService);
                    }
                }
            }
            if (string.IsNullOrWhiteSpace(dartString.ToString()))
            {
                return null;
            }
            var serviceModelDartString = new StringBuilder();
            foreach (var importService in importServiceList)
            {
                serviceModelDartString.AppendLine($"import '{LetterConverterHelper.FirstCharToLower(importService[0..^7])}.m.dart';");
            }
            if (importServiceList.Count > 0)
            {
                serviceModelDartString.AppendLine();
            }

            var dartStringInfo = dartString.ToString();
            if (dartStringInfo.Contains("JsonRpcUtils"))
            {
                serviceModelDartString.AppendLine(CommonParameters.StringUtils);
                serviceModelDartString.AppendLine();
            }

            if (dartStringInfo.Contains("FJsonConvert"))
            {
                serviceModelDartString.AppendLine(CommonParameters.StringJsonConvert);
                serviceModelDartString.AppendLine();
            }
            serviceModelDartString.AppendLine(dartStringInfo);
            return serviceModelDartString.ToString();
        }

        public string GetDecodeDartString()
        {
            var dartString = new StringBuilder();
            var importServiceList = new List<string>();
            var alreadyGeneratedList = GenerateDataCache.Instance.AlreadyGeneratedList;
            dartString.AppendLine("typedef _ModelBuilder<T> = T Function(Map<String, dynamic> map);");
            dartString.AppendLine("class NotificationDecoder {");
            dartString.AppendLine("\tNotificationDecoder._();");
            dartString.AppendLine("");
            dartString.AppendLine("\tstatic final _builders = _ModelBuilderCollection();");
            dartString.AppendLine("");
            dartString.AppendLine("\tstatic T decode<T>(Map<String, dynamic> map) {");
            dartString.AppendLine("\t\tfinal type = decodeType(map);");
            dartString.AppendLine("\t\tfinal builder = _builders.find(type);");
            dartString.AppendLine("\t\tfinal model = builder.call(map) as T;");
            dartString.AppendLine("\t\treturn model;");
            dartString.AppendLine("\t}");
            dartString.AppendLine($"\tstatic {_notificationEnumModelType.Name_Upper} decodeType(Map<String, dynamic> map) {{");
            dartString.AppendLine("\t\tfinal typeInt = map['NotificationType'] as int;");
            dartString.AppendLine($"\t\treturn {_notificationEnumModelType.Name_Upper}.values[typeInt];");
            dartString.AppendLine("\t}");
            dartString.AppendLine("");
            dartString.AppendLine("\tstatic void setup() {");
            dartString.AppendLine("\t\t/** Register notification model builders here */");
            if (alreadyGeneratedList.Any(x => x.Key.ParameterType.Name == _notificationEnumModelType.ParameterType.Name && x.Key.ParameterType.Namespace == _notificationEnumModelType.ParameterType.Namespace))
            {
                var importService = alreadyGeneratedList.FirstOrDefault(x => x.Key.ParameterType.Name == _notificationEnumModelType.ParameterType.Name && x.Key.ParameterType.Namespace == _notificationEnumModelType.ParameterType.Namespace).Value;
                if (importService == null)
                {
                    throw new Exception("Import Service is null");
                }
                if (!importServiceList.Contains(importService))
                {
                    importServiceList.Add(importService);
                }
            }
            foreach (var notificationModelType in _notificationModelTypeList)
            {
                if (alreadyGeneratedList.Any(x => x.Key.ParameterType.Name == notificationModelType.ParameterType.Name && x.Key.ParameterType.Namespace == notificationModelType.ParameterType.Namespace))
                {
                    var importService = alreadyGeneratedList.FirstOrDefault(x => x.Key.ParameterType.Name == notificationModelType.ParameterType.Name && x.Key.ParameterType.Namespace == notificationModelType.ParameterType.Namespace).Value;
                    if (importService == null)
                    {
                        throw new Exception("Import Service is null");
                    }
                    if (!importServiceList.Contains(importService))
                    {
                        importServiceList.Add(importService);
                    }
                }
                if (notificationModelType.ParameterType.IsAbstract)
                {
                    continue;
                }
                var instance = Activator.CreateInstance(notificationModelType.ParameterType);
                var instanceString = JsonSerializer.Serialize(instance);
                try
                {
                    var notificationType = JsonSerializer.Deserialize<VirtualNotification>(instanceString);
                    if (notificationType != null)
                    {
                        var value = _notificationEnumModelType.UserDefinedEnumDictionary[notificationType.NotificationType];
                        dartString.AppendLine($"\t\t_builders.add({_notificationEnumModelType.Name_Upper}.{value},");
                        dartString.AppendLine($"\t\t\t\t(map) => {notificationModelType.Name_Upper}.fromJson(map));");
                        dartString.AppendLine("");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Notification Deserialize Error:{ex}");
                    continue;
                }
            }
            dartString.AppendLine("\t}");
            dartString.AppendLine("}");
            dartString.AppendLine("");
            dartString.AppendLine("class _ModelBuilderCollection {");
            dartString.AppendLine($"\tfinal Map<{_notificationEnumModelType.Name_Upper}, _ModelBuilder> _source = {{}};");
            dartString.AppendLine("");
            dartString.AppendLine($"\tvoid add({_notificationEnumModelType.Name_Upper} type, _ModelBuilder builder) {{");
            dartString.AppendLine("\t\t_source[type] = builder;");
            dartString.AppendLine("\t}");
            dartString.AppendLine("");
            dartString.AppendLine($"\t_ModelBuilder find({_notificationEnumModelType.Name_Upper} type) {{");
            dartString.AppendLine("\t\tfinal builder = _source[type];");
            dartString.AppendLine("\t\treturn builder!;");
            dartString.AppendLine("\t}");
            dartString.AppendLine("}");
            var decodeDartString = new StringBuilder();
            foreach (var importService in importServiceList)
            {
                decodeDartString.AppendLine($"import '{LetterConverterHelper.FirstCharToLower(importService[0..^7])}.m.dart';");
            }
            if (importServiceList.Count > 0)
            {
                decodeDartString.AppendLine();
            }
            decodeDartString.AppendLine(dartString.ToString());
            return decodeDartString.ToString();
        }
    }
}