using System;
using System.Collections.Generic;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.RepresentationModel;

namespace PCL.Core.App.Configuration.Storage;

// Partly generated by gpt-5 (20250903)
public sealed class YamlNodeEmitter : IEmitter
{
    private readonly List<YamlDocument> _documents = [];
    private readonly Stack<Container> _stack = new();
    private readonly Dictionary<string, YamlNode> _anchors = new(StringComparer.Ordinal);
    private bool _inStream;

    public IReadOnlyList<YamlDocument> Documents => _documents;

    public YamlNode SingleRootNode => _documents.Count switch
    {
        0 => throw new InvalidOperationException("尚未产生任何文档。"),
        1 => _documents[0].RootNode,
        _ => throw new InvalidOperationException("存在多个文档，请使用 Documents 访问。")
    };

    public void Reset()
    {
        _documents.Clear();
        _stack.Clear();
        _anchors.Clear();
        _inStream = false;
    }

    public void Emit(ParsingEvent @event)
    {
        switch (@event)
        {
            case StreamStart:
                Reset();
                _inStream = true;
                break;

            case StreamEnd:
                _inStream = false;
                if (_stack.Count != 0)
                    throw new InvalidOperationException("事件结束时堆栈未清空，事件序列不平衡。");
                break;

            case DocumentStart:
                _RequireStream();
                _stack.Push(Container.Document());
                break;

            case DocumentEnd:
                {
                    _RequireStream();
                    if (_stack.Count == 0 || _stack.Peek().Kind != ContainerKind.Document)
                        throw new InvalidOperationException("DocumentEnd 前缺少对应的 DocumentStart。");

                    var doc = _stack.Pop();
                    if (doc.Node is null)
                        throw new InvalidOperationException("空文档：未设置根节点。");

                    _documents.Add(new YamlDocument(doc.Node));
                }
                break;

            case MappingStart mapStart:
                {
                    var map = new YamlMappingNode();
                    _ApplyAnchor(mapStart, map);
                    _AttachToParent(map);
                    _stack.Push(Container.Mapping(map));
                }
                break;

            case MappingEnd:
                {
                    if (_stack.Count == 0 || _stack.Peek().Kind != ContainerKind.Mapping)
                        throw new InvalidOperationException("MappingEnd 前缺少对应的 MappingStart。");

                    var finished = _stack.Pop();
                    // no-op: 已在 Start 时挂接到父级
                    if (finished.PendingKey is not null)
                        throw new InvalidOperationException("映射以键结尾，缺少对应的值。");
                }
                break;

            case SequenceStart seqStart:
                {
                    var seq = new YamlSequenceNode();
                    _ApplyAnchor(seqStart, seq);
                    _AttachToParent(seq);
                    _stack.Push(Container.Sequence(seq));
                }
                break;

            case SequenceEnd:
                {
                    if (_stack.Count == 0 || _stack.Peek().Kind != ContainerKind.Sequence)
                        throw new InvalidOperationException("SequenceEnd 前缺少对应的 SequenceStart。");

                    _stack.Pop(); // 已在 Start 时挂接到父级
                }
                break;

            case Scalar scalar:
                {
                    var node = new YamlScalarNode(scalar.Value);
                    _ApplyAnchor(scalar, node);
                    _AttachToParent(node);
                }
                break;

            case AnchorAlias alias:
                {
                    var anchorName = alias.Value.Value; // AnchorName.Value -> string
                    if (!_anchors.TryGetValue(anchorName, out var target))
                        throw new InvalidOperationException($"未找到锚点 '{anchorName}' 的定义。");
                    _AttachToParent(target);
                }
                break;
        }
    }

    private void _RequireStream()
    {
        if (!_inStream)
            throw new InvalidOperationException("必须在 StreamStart 与 StreamEnd 之间接收事件。");
    }

    private void _ApplyAnchor(NodeEvent nodeEvent, YamlNode node)
    {
        // 仅在存在锚点时登记；Tag/Style 等可按需扩展
        if (nodeEvent.Anchor.IsEmpty) return;
        var name = nodeEvent.Anchor.Value;
        node.Anchor = name;
        // 最新 YamlDotNet 表示模型允许同名锚点复用同一节点引用；
        // 若重复定义同名锚点，认为是非法
        if (!_anchors.TryAdd(name, node))
            throw new InvalidOperationException($"锚点 '{name}' 被重复定义。");
    }

    private void _AttachToParent(YamlNode node)
    {
        if (_stack.Count == 0)
        {
            throw new InvalidOperationException("缺少 DocumentStart：无法确定根节点所属文档。");
        }

        var parent = _stack.Peek();
        switch (parent.Kind)
        {
            case ContainerKind.Document:
                if (parent.Node is not null)
                    throw new InvalidOperationException("一个文档只能包含一个根节点。");
                parent.Node = node;
                _stack.Pop();
                _stack.Push(parent); // 写回修改
                break;

            case ContainerKind.Sequence:
                ((YamlSequenceNode)parent.Node!).Add(node);
                break;

            case ContainerKind.Mapping:
                if (parent.PendingKey is null)
                {
                    parent.PendingKey = node; // 作为键
                }
                else
                {
                    ((YamlMappingNode)parent.Node!).Add(parent.PendingKey, node);
                    parent.PendingKey = null;
                }
                _stack.Pop();
                _stack.Push(parent); // 写回修改
                break;

            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    private enum ContainerKind { Document, Mapping, Sequence }

    private struct Container
    {
        public ContainerKind Kind;
        public YamlNode? Node;
        public YamlNode? PendingKey;

        public static Container Document() => new() { Kind = ContainerKind.Document, Node = null, PendingKey = null };
        public static Container Mapping(YamlMappingNode map) => new() { Kind = ContainerKind.Mapping, Node = map, PendingKey = null };
        public static Container Sequence(YamlSequenceNode seq) => new() { Kind = ContainerKind.Sequence, Node = seq, PendingKey = null };
    }
}
