using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Tomlyn;
using Tomlyn.Model;

namespace SessionZero.Data.Sztl;

public static class SztlParser
{
    public static SzTemplate ParseTemplate(string tomlText)
{
    var doc = Toml.ToModel(tomlText);


    if (!doc.ContainsKey("metadata") || doc["metadata"] is not TomlTable metadata)
    {
        throw new Exception("Template missing the [metadata] section");
    }

    // Determine template type and fill metadata
    string templateType = metadata.TryGetValue("template_type", out var tempTypeVal)
        ? tempTypeVal?.ToString() ?? throw new Exception("Template missing template_type metadata")
        : throw new Exception("Template missing template_type metadata");
    
    SzTemplate template = templateType switch
    {
        "data" => new SzDataObjectTemplate(),
        _ => throw new NotImplementedException($"Unknown template_type '{templateType}'")
    };
    
    template.SztlText = tomlText.Trim();
    
    if (metadata.TryGetValue("id", out var idVal)) template.Id = idVal?.ToString()!;
    else throw new Exception("Template missing id metadata");

    if (metadata.TryGetValue("uuid", out var uuidVal)) template.Uuid = uuidVal?.ToString()!;
    else throw new Exception("Template missing uuid metadata");

    if (metadata.TryGetValue("version", out var versionVal)) template.Version = versionVal?.ToString()!;
    else throw new Exception("Template missing version metadata");

    if (metadata.TryGetValue("compatible_systems", out var compSysVal) && compSysVal is string[] arr)
        template.CompatibleSystems = arr.ToList();

    if (metadata.TryGetValue("description", out var descVal))
        template.Description = descVal?.ToString()!;
    else throw new Exception("Template missing description metadata");
    
    template.ParseAdditionalMetaData(doc);

    // Process all other tables as fields or groups
    ProcessTable(template, doc);

    return template;
}

    
    
    private static void InsertFieldIntoGroup(SzTemplate template, string[] pathParts, TomlTable table)
    {
        var groupDict = template.SubGroups;
        SztlFieldGroup currentGroup = null!;
        
        for (int i = 0; i < pathParts.Length - 1; i++)
        {
            var part = pathParts[i];

            if (!groupDict.TryGetValue(part, out var grp))
            {
                grp = new SztlFieldGroup { Id = part };
                groupDict[part] = grp;
            }
            currentGroup = grp;
            groupDict = grp.SubGroups;
        }
        
        var fieldId = pathParts[^1];
        var leafTable = table;

        if (table.TryGetValue(fieldId, out var possibleLeaf) && possibleLeaf is TomlTable t)
        {
            leafTable = t;
        }

        var field = ParseFieldFromTable(fieldId, leafTable);
        currentGroup.Fields[fieldId] = field;
    }


    private static SztlField ParseFieldFromTable(string id, TomlTable table)
    {
        var field = new SztlField
        {
            Id = id
        };

        if (!table.TryGetValue("type", out var tVal))
        {
            throw new Exception($"Field '{id}' missing type");
        }

        var typeStr = tVal?.ToString() ?? throw new Exception($"Field '{id}' has null type");
        if (!Enum.TryParse<SzFieldType>(typeStr, ignoreCase: true, out var ftype))
        {
            throw new Exception($"Field '{id}' has unknown type '{typeStr}'");
        }
        field.Type = ftype;
        
        if (table.TryGetValue("default_value", out var dVal))
        {
            field.DefaultValue = dVal;
        }
        
        if (ftype == SzFieldType.Textblock)
        {
            field.IsTextBlock = true;
        }

        return field;
    }
    
    private static void ProcessTable(SzTemplate template, TomlTable table, string parentPath = "")
    {
        foreach (var (key, value) in table)
        {
            if (key == "metadata") continue;

            var path = string.IsNullOrEmpty(parentPath) ? key : $"{parentPath}.{key}";

            if (value is TomlTable t)
            {
                if (t.ContainsKey("type"))
                {
                    var field = ParseFieldFromTable(key, t);

                    if (string.IsNullOrEmpty(parentPath))
                    {
                        template.Fields[key] = field;
                    }
                    else
                    {
                        InsertFieldIntoGroup(template, path.Split('.'), t);
                    }
                }
                else
                {
                    ProcessTable(template, t, path);
                }
            }
        }
    }
}