using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace EonaCat.LogStack.Destructuring;

// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.

/// <summary>
/// Renders complex types to JSON-like format without external dependencies
/// </summary>
public sealed class TypeDestructor
{
    private readonly HashSet<object> _visited = new();
    private const int MaxDepth = 10;
    private const int MaxStringLength = 1000;
    private const int MaxCollectionItems = 100;

    /// <summary>
    /// Destructures an object to a string representation (JSON-like format)
    /// </summary>
    public string Destructure(object? value)
    {
        _visited.Clear();
        var sb = new StringBuilder();
        DestructureValue(value, sb, 0);
        return sb.ToString();
    }

    private void DestructureValue(object? value, StringBuilder sb, int depth)
    {
        if (depth > MaxDepth)
        {
            sb.Append("\"...\"");
            return;
        }

        if (value == null)
        {
            sb.Append("null");
            return;
        }

        var type = value.GetType();

        // Handle primitives
        if (type == typeof(string))
        {
            AppendString(sb, (string)value);
            return;
        }

        if (type == typeof(bool))
        {
            sb.Append(((bool)value) ? "true" : "false");
            return;
        }

        if (type == typeof(byte) || type == typeof(sbyte) ||
            type == typeof(short) || type == typeof(ushort) ||
            type == typeof(int) || type == typeof(uint) ||
            type == typeof(long) || type == typeof(ulong))
        {
            sb.Append(value);
            return;
        }

        if (type == typeof(float) || type == typeof(double) || type == typeof(decimal))
        {
            sb.Append(((IFormattable)value).ToString(null, CultureInfo.InvariantCulture));
            return;
        }

        if (type == typeof(DateTime))
        {
            sb.Append("\"").Append(((DateTime)value).ToIso8601String()).Append("\"");
            return;
        }

        if (type == typeof(DateTimeOffset))
        {
            sb.Append("\"").Append(((DateTimeOffset)value).ToString("O")).Append("\"");
            return;
        }

        if (type == typeof(TimeSpan))
        {
            sb.Append("\"").Append(((TimeSpan)value).ToString()).Append("\"");
            return;
        }

        if (type == typeof(Guid))
        {
            sb.Append("\"").Append(((Guid)value).ToString()).Append("\"");
            return;
        }

        // Check for circular references
        if (_visited.Contains(value))
        {
            sb.Append("\"<circular reference>\"");
            return;
        }

        _visited.Add(value);

        // Handle collections
        if (value is IEnumerable enumerable && !(value is string))
        {
            DestructureCollection(enumerable, sb, depth);
            return;
        }

        // Handle objects
        DestructureObject(value, sb, depth);
    }

    private void DestructureCollection(IEnumerable enumerable, StringBuilder sb, int depth)
    {
        sb.Append("[");
        int count = 0;

        foreach (var item in enumerable)
        {
            if (count > 0)
            {
                sb.Append(",");
            }

            if (count >= MaxCollectionItems)
            {
                sb.Append("\"...\"");
                break;
            }

            DestructureValue(item, sb, depth + 1);
            count++;
        }

        sb.Append("]");
    }

    private void DestructureObject(object value, StringBuilder sb, int depth)
    {
        sb.Append("{");

        var properties = value.GetType().GetProperties();
        int count = 0;

        foreach (var prop in properties)
        {
            if (count > 0)
            {
                sb.Append(",");
            }

            sb.Append("\"").Append(EscapeJsonString(prop.Name)).Append("\":");

            try
            {
                var propValue = prop.GetValue(value);
                DestructureValue(propValue, sb, depth + 1);
            }
            catch
            {
                sb.Append("\"<error>\"");
            }

            count++;

            if (count >= MaxCollectionItems)
            {
                sb.Append(",\"...\":null");
                break;
            }
        }

        sb.Append("}");
    }

    private void AppendString(StringBuilder sb, string value)
    {
        sb.Append("\"");

        int length = Math.Min(value.Length, MaxStringLength);
        for (int i = 0; i < length; i++)
        {
            char c = value[i];
            sb.Append(EscapeChar(c));
        }

        if (value.Length > MaxStringLength)
        {
            sb.Append("...\"");
        }
        else
        {
            sb.Append("\"");
        }
    }

    private static string EscapeJsonString(string value)
    {
        var sb = new StringBuilder();
        foreach (char c in value)
        {
            sb.Append(EscapeChar(c));
        }
        return sb.ToString();
    }

    private static string EscapeChar(char c)
    {
        return c switch
        {
            '"' => "\\\"",
            '\\' => "\\\\",
            '\b' => "\\b",
            '\f' => "\\f",
            '\n' => "\\n",
            '\r' => "\\r",
            '\t' => "\\t",
            _ => c < 32 ? $"\\u{(int)c:X4}" : c.ToString()
        };
    }
}

/// <summary>
/// Specialized destructors for common types
/// </summary>
public sealed class SpecializedDestructor
{
    /// <summary>
    /// Destructures an exception with full context
    /// </summary>
    public static string DestructureException(Exception ex)
    {
        var sb = new StringBuilder();
        sb.Append("{");
        sb.Append("\"Type\":\"").Append(EscapeJsonString(ex.GetType().Name)).Append("\",");
        sb.Append("\"Message\":\"").Append(EscapeJsonString(ex.Message)).Append("\",");
        sb.Append("\"StackTrace\":\"").Append(EscapeJsonString(ex.StackTrace ?? "")).Append("\"");

        if (ex.InnerException != null)
        {
            sb.Append(",\"InnerException\":").Append(DestructureException(ex.InnerException));
        }

        sb.Append("}");
        return sb.ToString();
    }

    /// <summary>
    /// Destructures a key-value pair collection
    /// </summary>
    public static string DestructureKeyValuePairs<TKey, TValue>(
        IEnumerable<KeyValuePair<TKey, TValue>> pairs)
    {
        var sb = new StringBuilder();
        sb.Append("{");

        int count = 0;
        foreach (var kvp in pairs)
        {
            if (count > 0)
            {
                sb.Append(",");
            }

            sb.Append("\"").Append(EscapeJsonString(kvp.Key?.ToString() ?? "null")).Append("\":");
            sb.Append("\"").Append(EscapeJsonString(kvp.Value?.ToString() ?? "null")).Append("\"");

            count++;
            if (count >= 100)
            {
                break;
            }
        }

        sb.Append("}");
        return sb.ToString();
    }

    private static string EscapeJsonString(string value)
    {
        var sb = new StringBuilder();
        foreach (char c in value)
        {
            sb.Append(c switch
            {
                '"' => "\\\"",
                '\\' => "\\\\",
                '\b' => "\\b",
                '\f' => "\\f",
                '\n' => "\\n",
                '\r' => "\\r",
                '\t' => "\\t",
                _ => c < 32 ? $"\\u{(int)c:X4}" : c.ToString()
            });
        }
        return sb.ToString();
    }
}

/// <summary>
/// Extension methods for type destructuring
/// </summary>
public static class DestructuringExtensions
{
    /// <summary>
    /// Converts an object to its destructured JSON representation
    /// </summary>
    public static string ToDestructuredJson(this object? value)
    {
        if (value == null)
        {
            return "null";
        }

        var destructor = new TypeDestructor();
        return destructor.Destructure(value);
    }

    /// <summary>
    /// Escapes special characters in a string for JSON output
    /// </summary>
    public static string ToJsonString(this string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return "\"\"";
        }

        var sb = new StringBuilder();
        sb.Append("\"");
        foreach (char c in value)
        {
            sb.Append(c switch
            {
                '"' => "\\\"",
                '\\' => "\\\\",
                '\b' => "\\b",
                '\f' => "\\f",
                '\n' => "\\n",
                '\r' => "\\r",
                '\t' => "\\t",
                _ => c < 32 ? $"\\u{(int)c:X4}" : c.ToString()
            });
        }
        sb.Append("\"");
        return sb.ToString();
    }
}

/// <summary>
/// Extension for ISO 8601 formatting
/// </summary>
public static class DateTimeExtensionForDestructuring
{
    public static string ToIso8601String(this DateTime dt)
    {
        return dt.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
    }
}
