using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace EonaCat.LogStack.Structured;

// 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>
/// Represents a structured property with type information
/// </summary>
public sealed class StructuredProperty
{
    public string Name { get; set; }
    public object? Value { get; set; }
    public Type? ValueType { get; set; }
    public bool ShouldDestructure { get; set; }

    public StructuredProperty(string name, object? value, bool shouldDestructure = false)
    {
        Name = name;
        Value = value;
        ValueType = value?.GetType();
        ShouldDestructure = shouldDestructure;
    }
}

/// <summary>
/// Builder for structured properties using Serilog-like syntax
/// </summary>
public sealed class StructuredPropertyBuilder
{
    private readonly Dictionary<string, StructuredProperty> _properties = new();

    /// <summary>
    /// Adds a simple property
    /// </summary>
    public StructuredPropertyBuilder Add(string name, object? value)
    {
        _properties[name] = new StructuredProperty(name, value, false);
        return this;
    }

    /// <summary>
    /// Adds a property that should be destructured (rendered as JSON)
    /// </summary>
    public StructuredPropertyBuilder AddDestructured(string name, object? value)
    {
        _properties[name] = new StructuredProperty(name, value, true);
        return this;
    }

    /// <summary>
    /// Adds multiple properties from an anonymous object
    /// Example: .AddFromAnonymous(new { UserId = 123, Action = "Login" })
    /// </summary>
    public StructuredPropertyBuilder AddFromAnonymous(object anonymous)
    {
        if (anonymous == null)
        {
            return this;
        }

        var properties = anonymous.GetType().GetProperties();
        foreach (var prop in properties)
        {
            try
            {
                var value = prop.GetValue(anonymous);
                Add(prop.Name, value);
            }
            catch
            {
                // Ignore properties that can't be read
            }
        }

        return this;
    }

    /// <summary>
    /// Adds multiple properties from a dictionary
    /// </summary>
    public StructuredPropertyBuilder AddFromDictionary(Dictionary<string, object?> dict)
    {
        if (dict == null)
        {
            return this;
        }

        foreach (var kvp in dict)
        {
            Add(kvp.Key, kvp.Value);
        }

        return this;
    }

    /// <summary>
    /// Removes a property
    /// </summary>
    public StructuredPropertyBuilder Remove(string name)
    {
        _properties.Remove(name);
        return this;
    }

    /// <summary>
    /// Clears all properties
    /// </summary>
    public StructuredPropertyBuilder Clear()
    {
        _properties.Clear();
        return this;
    }

    /// <summary>
    /// Gets the built dictionary
    /// </summary>
    public Dictionary<string, object?> Build()
    {
        return _properties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Value);
    }

    /// <summary>
    /// Gets the structured properties
    /// </summary>
    public Dictionary<string, StructuredProperty> BuildStructured()
    {
        return new Dictionary<string, StructuredProperty>(_properties);
    }

    /// <summary>
    /// Gets count of properties
    /// </summary>
    public int Count => _properties.Count;
}

/// <summary>
/// Contextual property scope that inherits to nested calls
/// Similar to Serilog's LogContext.PushProperty
/// </summary>
public sealed class PropertyScope : IDisposable
{
    private readonly Stack<Dictionary<string, object?>> _scopeStack;
    private readonly Dictionary<string, object?> _currentScope;

    private static readonly AsyncLocal<PropertyScope?> _currentScopeHolder = new();

    public PropertyScope(Dictionary<string, object?> initialProperties)
    {
        _scopeStack = new Stack<Dictionary<string, object?>>();
        _currentScope = new Dictionary<string, object?>(initialProperties);
        _scopeStack.Push(_currentScope);

        var previous = _currentScopeHolder.Value;
        _currentScopeHolder.Value = this;
    }

    public void PushScope(Dictionary<string, object?> properties)
    {
        var newScope = new Dictionary<string, object?>(_scopeStack.Peek());
        foreach (var kvp in properties)
        {
            newScope[kvp.Key] = kvp.Value;
        }
        _scopeStack.Push(newScope);
    }

    public void PopScope()
    {
        if (_scopeStack.Count > 1)
        {
            _scopeStack.Pop();
        }
    }

    public Dictionary<string, object?> GetCurrentProperties()
    {
        if (_scopeStack.Count == 0)
        {
            return new Dictionary<string, object?>();
        }

        return new Dictionary<string, object?>(_scopeStack.Peek());
    }

    public static Dictionary<string, object?> GetActiveProperties()
    {
        var scope = _currentScopeHolder.Value;
        return scope?.GetCurrentProperties() ?? new Dictionary<string, object?>();
    }

    public void Dispose()
    {
        _scopeStack.Clear();
        if (_currentScopeHolder.Value == this)
        {
            _currentScopeHolder.Value = null;
        }
    }
}

/// <summary>
/// Extensions for adding properties to LogEvent
/// </summary>
public static class StructuredPropertyExtensions
{
    // Note: These extension methods are intended to extend LogEventBuilder
    // from EonaCat.LogStack.Core once that type is available.
    // For now, they are provided as examples for integration patterns.
}
