using System;
using System.Collections.Generic;
using System.Text;

namespace UVC.Extention
{
    /// <summary>
    /// Dictionary 클래스에 대한 확장 메소드를 제공하는 클래스입니다.
    /// </summary>
    public static class DictionaryEx
    {
        /// <summary>
        /// Dictionary를 JSON 문자열로 변환합니다.
        /// 중첩된 Dictionary 객체도 재귀적으로 처리합니다.
        /// </summary>
        /// <typeparam name="TKey">Dictionary의 키 타입</typeparam>
        /// <typeparam name="TValue">Dictionary의 값 타입</typeparam>
        /// <param name="dict">JSON 문자열로 변환할 Dictionary 객체</param>
        /// <returns>JSON 형식의 문자열</returns>
        /// <example>
        /// <code>
        /// var dict = new Dictionary<string, object>
        /// {
        ///     { "name", "홍길동" },
        ///     { "age", 30 },
        ///     { "address", new Dictionary<string, string> { { "city", "서울" }, { "zip", "12345" } } }
        /// };
        /// 
        /// string json = dict.ToJson(); 
        /// // 결과: {"name":"홍길동","age":"30","address":{"city":"서울","zip":"12345"}}
        /// </code>
        /// </example>
        public static string ToJson<TKey, TValue>(this IDictionary<TKey, TValue> dict)
        {
            if (dict == null || dict.Count == 0)
                return "{}";
            var sb = new StringBuilder("{");
            foreach (var kvp in dict)
            {
                sb.Append($"\"{kvp.Key}\":");

                // 값이 null인 경우 처리
                if (kvp.Value == null)
                {
                    sb.Append("null,");
                    continue;
                }

                // 값이 중첩된 Dictionary인 경우 재귀적으로 처리
                if (kvp.Value is IDictionary<object, object> dictObject)
                {
                    string nestedJson = ToJson(dictObject);
                    sb.Append($"{nestedJson},");
                }
                else if (kvp.Value is IDictionary<string, object> dictStringObject)
                {
                    string nestedJson = ToJson(dictStringObject);
                    sb.Append($"{nestedJson},");
                }
                else if (kvp.Value is IDictionary<string, string> dictStringString)
                {
                    string nestedJson = ToJson(dictStringString);
                    sb.Append($"{nestedJson},");
                }
                // 값이 숫자인 경우 따옴표 없이 추가
                else if (IsNumericType(kvp.Value.GetType()))
                {
                    sb.Append($"{kvp.Value},");
                }
                // 값이 불리언인 경우 따옴표 없이 추가 (소문자 true/false)
                else if (kvp.Value is bool boolValue)
                {
                    sb.Append($"{boolValue.ToString().ToLowerInvariant()},");
                }
                // 그 외의 경우 문자열로 처리
                else
                {
                    // 특수 문자를 이스케이프 처리
                    string escapedValue = EscapeJsonString(kvp.Value.ToString());
                    sb.Append($"\"{escapedValue}\",");
                }
            }
            if (dict.Count > 0)
            {
                sb.Length--; // 마지막 쉼표(,) 제거
            }
            sb.Append("}");
            return sb.ToString();
        }

        /// <summary>
        /// 주어진 타입이 숫자형인지 확인합니다.
        /// </summary>
        /// <param name="type">검사할 타입</param>
        /// <returns>숫자형이면 true, 아니면 false</returns>
        private static bool IsNumericType(Type type)
        {
            if (type == null)
                return false;

            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Decimal:
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// JSON 문자열에서 특수 문자를 이스케이프 처리합니다.
        /// </summary>
        /// <param name="value">이스케이프 처리할 문자열</param>
        /// <returns>이스케이프 처리된 문자열</returns>
        private static string EscapeJsonString(string value)
        {
            if (string.IsNullOrEmpty(value))
                return value;

            StringBuilder sb = new StringBuilder();
            foreach (char c in value)
            {
                switch (c)
                {
                    case '\\': sb.Append("\\\\"); break;
                    case '\"': sb.Append("\\\""); break;
                    case '\n': sb.Append("\\n"); break;
                    case '\r': sb.Append("\\r"); break;
                    case '\t': sb.Append("\\t"); break;
                    case '\b': sb.Append("\\b"); break;
                    case '\f': sb.Append("\\f"); break;
                    default:
                        if (c < 32) // 제어 문자
                        {
                            sb.Append($"\\u{(int)c:X4}");
                        }
                        else
                        {
                            sb.Append(c);
                        }
                        break;
                }
            }
            return sb.ToString();
        }
    }
}
