using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Linq;
using System;
using System.IO;
// using Newtonsoft.Json;

namespace AscNet.Table
{
    [Generator]
    public class TableGeneratorV2 : ISourceGenerator
    {
        public void Execute(GeneratorExecutionContext context)
        {
            List<object> fails = new();
            foreach (var table in context.AdditionalFiles.Where(x => x.Path.EndsWith(".tsv")))
            {
                try
                {
                    string ns = string.Join("", Path.GetDirectoryName(table.Path)?.Split(new string[] { "table" }, StringSplitOptions.None).Skip(1)!)
                        .Replace('\\', '/').Replace('/', '.');

                    string content = (table.GetText()?.ToString()) ?? throw new FormatException("File is empty!");
                    List<string> lines = content.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList();

                    List<MemberType> members = new();
                    List<MemberType> membersRealCount = new();
                    string headLine = lines.First();
                    lines.RemoveAt(0);

                    foreach (var head in headLine.Split('\t'))
                    {
                        if (head.Contains('['))
                        {
                            if (members.Any(x => x.Name == head.Split('[').First()))
                            {
                                MemberType memberType = members.First(x => x.Name == head.Split('[').First());
                                memberType.Span++;
                            }
                            else
                            {
                                members.Add(new MemberType
                                {
                                    Name = head.Split('[').First(),
                                    Nullable = false,
                                    Span = 1
                                });
                            }
                        }
                        else
                        {
                            members.Add(new MemberType
                            {
                                Name = head,
                                Nullable = false,
                                Span = 1
                            });
                        }
                        membersRealCount.Add(members.First(x => x.Name == head.Split('[').First()));
                    }

                    for (int x = 0; x < lines.Count; x++)
                    {
                        string[] values = lines[x].Split('\t');

                        for (int y = 0; y < values.Length; y++)
                        {
                            string value = values[y];
                            MemberType memberType = membersRealCount[y];
                            if (string.IsNullOrEmpty(value))
                            {
                                memberType.Nullable = true;
                            }
                            else
                            {
                                if (int.TryParse(value, out int intVal))
                                {
                                    if (memberType.Type != typeof(string).FullName)
                                        memberType.Type = typeof(int).FullName;
                                }
                                else
                                {
                                    memberType.Type = typeof(string).FullName;
                                }
                            }
                            membersRealCount[y] = memberType;
                        }
                    }

                    string propDefs = string.Empty;
                    foreach (MemberType memberType in membersRealCount.GroupBy(x => x.Name).Select(g => g.First()))
                    {
                        if (string.IsNullOrEmpty(memberType.Type) || string.IsNullOrEmpty(memberType.Name))
                            continue;

                        if (membersRealCount.Where(x => x.Name == memberType.Name).Count() > 1)
                        {
                            propDefs += $"public List<{memberType.Type}> {memberType.Name} {{ get; set; }}\r\n\t";
                        }
                        else
                        {
                            propDefs += $"public {memberType.Type + (memberType.Nullable ? "?" : string.Empty)} {memberType.Name} {{ get; set; }}\r\n\t";
                        }
                    }

                    string file = $@"// <auto-generated/>
namespace AscNet.Table.V2{ns}
{{
    #nullable enable
    #pragma warning disable CS8618, CS8602 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
    public class {Path.GetFileNameWithoutExtension(table.Path)}Table : global::AscNet.Common.Util.ITable
    {{
        public static string File => ""{string.Join("", table.Path.Replace("\\", "/").Split(new string[] { "/Resources/" }, StringSplitOptions.None).Skip(1))}"";

        {propDefs}}}
}}";
                    
                    context.AddSource($"V2{ns}.{Path.GetFileNameWithoutExtension(table.Path)}V2.g.cs", file);
                }
                catch (Exception ex)
                {

                    fails.Add(new
                    {
                        msg = ex.ToString(),
                        file = table.Path
                    });
                }
            }

            // File.WriteAllText("./generator_fails_new.json", JsonConvert.SerializeObject(fails, Formatting.Indented));
        }

        public void Initialize(GeneratorInitializationContext context) { }

        struct MemberType
        {
            public string Name { get; set; }
            public string Type { get; set; }
            public bool Nullable { get; set; }
            public int Span {  get; set; }
        }
    }
}
