using System.Diagnostics.CodeAnalysis;
using Voile.Resources;
using Voile.Resources.DataReaders;
using Voile.UI.Containers;
using Voile.VFS;

namespace Voile.UI;

/// <summary>
/// UI style settings.
/// </summary>
public class Style
{
    public enum TransitionType
    {
        Linear,
        EaseIn,
        EaseOut,
        EaseInOut
    }

    public float TransitionDuration = 0f;
    public TransitionType Transition = TransitionType.Linear;

    public Style() { }

    public Size? Padding { get; set; }
    public Color? BackgroundColor { get; set; }
    public Size? BorderSize { get; set; }
    public Color? BorderColor { get; set; }
    public float CornerRadius { get; set; }
    public Color? TextColor { get; set; }

    /// <summary>
    /// Merges this <see cref="Style"/> with a different one.<br />
    /// Properties that are not set for this <see cref="Style"/> will be inherited from <paramref name="overrideStyle"/>.
    /// </summary>
    /// <param name="other"></param>
    /// <returns>A merged <see cref="Style"/>.</returns>
    public Style Merge(Style other)
    {
        return new Style
        {
            Padding =
                other.Padding ?? Padding,

            BackgroundColor =
                other.BackgroundColor ?? BackgroundColor,

            BorderSize =
                other.BorderSize ?? BorderSize,

            BorderColor =
                other.BorderColor ?? BorderColor,

            TextColor =
                other.TextColor ?? TextColor,

            CornerRadius =
                other.CornerRadius != default
                    ? other.CornerRadius
                    : CornerRadius,

            TransitionDuration =
                other.TransitionDuration != default
                    ? other.TransitionDuration
                    : TransitionDuration,

            Transition =
                other.Transition
        };
    }
}

public class StyleSheetLoader : ResourceLoader<StyleSheet>
{
    public override IEnumerable<string> SupportedExtensions => [".toml"];

    protected override StyleSheet LoadResource(string path)
    {
        var result = new StyleSheet(path);
        var allStyles = new Dictionary<string, Style>();

        using var stream = VirtualFileSystem.Read(path);
        _reader.Read(stream);

        foreach (var styleKey in _reader.GetSubKeysRecursive())
        {
            var subReader = _reader.GetSubReader(styleKey);
            if (subReader == null || !subReader.Valid())
                continue;

            var style = ParseStyle(subReader, styleKey);
            if (style != null)
            {
                allStyles[styleKey] = style;
            }
        }

        foreach (var kvp in allStyles)
        {
            var finalStyle = GetMergedStyle(kvp.Key, allStyles);
            result.Add(kvp.Key, finalStyle);
        }

        return result;
    }

    private Style ParseStyle(TomlDataReader reader, string input)
    {
        var style = new Style();

        string transitionName = reader.GetString("TransitionType", "Linear");

        if (!Enum.TryParse<Style.TransitionType>(transitionName, true, out var transition))
            throw new ArgumentException($"\"{transitionName}\" is not a valid TransitionType.");

        style.Transition = transition;


        if (reader.HasKey("BackgroundColor"))
            style.BackgroundColor = reader.GetColor("BackgroundColor", Color.Transparent);

        if (reader.HasKey("TextColor"))
            style.TextColor = reader.GetColor("TextColor", Color.Black);

        if (reader.HasKey("Padding"))
            style.Padding = reader.GetSize("Padding", Size.Zero);

        if (reader.HasKey("BorderSize"))
            style.BorderSize = reader.GetSize("BorderSize", Size.Zero);

        if (reader.HasKey("BorderColor"))
            style.BorderColor = reader.GetColor("BorderColor", Color.Transparent);

        return style;
    }

    private Style GetMergedStyle(string fullKey, Dictionary<string, Style> allStyles)
    {
        var parts = fullKey.Split('.');
        var merged = new Style();

        for (int i = 1; i <= parts.Length; i++)
        {
            var subKey = string.Join('.', parts.Take(i));
            if (allStyles.TryGetValue(subKey, out var parentStyle))
            {
                merged = merged.Merge(parentStyle);
            }
        }

        return merged;
    }

    private readonly TomlDataReader _reader = new();
}

public class StyleSheet : Resource
{
    public StyleSheet(string path) : base(path)
    {
    }

    public StyleSheet(Dictionary<string, Style> styles) : base(string.Empty)
    {
        _styles = styles;
    }

    public void Add(string key, Style style) => _styles.Add(key, style);

    public bool TryGet(
        string styleName,
        [NotNullWhen(true)] out Style? style)
    {
        style = Resolve(styleName);

        return style != null;
    }

    public static StyleSheet Default => new(new Dictionary<string, Style>()
    {
        {"Label", new Style()
        {
            TextColor = Color.FromHexString("#161616"),
            BackgroundColor = Color.DarkRed,
            BorderSize = new Size(2.0f),
            BorderColor = Color.Red
        }},
        { "Button", new Style()
        {
            Padding = new Size(8.0f),
            BackgroundColor = Color.FromHexString("#0f62fe"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Button.Normal", new Style()
        {
            Padding = new Size(8.0f),
            BackgroundColor = Color.FromHexString("#0f62fe"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Button.Hovered", new Style()
        {
            Padding = new Size(8.0f),
            BackgroundColor = Color.FromHexString("#0353e9"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Button.Pressed", new Style()
        {
            Padding = new Size(8.0f),
            BackgroundColor = Color.FromHexString("#002d9c"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Button.Danger", new Style()
        {
            Padding = new Size(8.0f),
            BackgroundColor = Color.FromHexString("#da1e28"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Button.Danger.Normal", new Style()
        {
            Padding = new Size(8.0f),
            BackgroundColor = Color.FromHexString("#da1e28"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Button.Danger.Hovered", new Style()
        {
            Padding = new Size(8.0f),
            BackgroundColor = Color.FromHexString("#ba1b23"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Button.Danger.Pressed", new Style()
        {
            Padding = new Size(8.0f),
            BackgroundColor = Color.FromHexString("#750e13"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Container", new Style()
        {
            BackgroundColor = Color.FromHexString("#ffffff"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Container.Layer01", new Style()
        {
            BackgroundColor = Color.FromHexString("#f4f4f4"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
        {"Container.Layer02", new Style()
        {
            BackgroundColor = Color.FromHexString("#e8e8e8"),
            TextColor = Color.FromHexString("#ffffff"),
        }},
    });

    private Style? Resolve(string fullKey)
    {
        var parts = fullKey.Split('.');

        Style? merged = null;

        for (int i = 1; i <= parts.Length; i++)
        {
            var subKey = string.Join('.',
                parts.Take(i));

            if (_styles.TryGetValue(
                subKey,
                out var style))
            {
                merged ??= new Style();

                merged = merged.Merge(style);
            }
        }

        return merged;
    }

    private Dictionary<string, Style> _styles = new();
}