using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using Aliyun.Api.LogService.Domain.Log;
using MrWu.Debug;
using Server.Core;

namespace Server.AliyunSDK.SLSLog
{
    public class SLSLogData : ISLSLogData,IReference
    {
        [SLSIgnore]
        public bool IsFromPool
        {
            get;
            set;
        }

        [SLSIgnore]
        public long ReferenceId
        {
            get;
            set;
        }
        
        [SLSIgnore]
        public DateTimeOffset Time { get; set; }
        
        private List<FieldInfo> _fields;
        private Dictionary<string, string> _fieldNameDict;
        
        public SLSLogData()
        {
            var fields = this.GetType().GetFields(System.Reflection.BindingFlags.Instance |
                                               System.Reflection.BindingFlags.Public |
                                               System.Reflection.BindingFlags.NonPublic |
                                               System.Reflection.BindingFlags.DeclaredOnly);
            _fields = new List<FieldInfo>(fields.Length);
            _fieldNameDict = new Dictionary<string, string>(fields.Length);
            foreach (var field in fields)
            { 
                if (Attribute.IsDefined(field, typeof(SLSIgnoreAttribute)))
                    continue;
                _fields.Add(field);
                var nameAttr = (SLSLogFiledNameAttribute)Attribute.GetCustomAttribute(field, typeof(SLSLogFiledNameAttribute));
                if (nameAttr != null && !string.IsNullOrEmpty(nameAttr.FieldName))
                {
                    _fieldNameDict.Add(field.Name, nameAttr.FieldName);
                }
            }
        }
        
        public virtual LogInfo Convert()
        {
            LogInfo logInfo = new LogInfo();
            logInfo.Time = Time;
            
            foreach (var field in _fields)
            {
                string key = field.Name;
                if (_fieldNameDict.TryGetValue(field.Name, out var newName))
                {
                    key = newName;
                }
                
                var attr = field.GetCustomAttribute<SLSLogTimeFormatAttribute>();
                var value = field.GetValue(this);
                if (attr != null && !string.IsNullOrWhiteSpace(attr.Format))
                {
                    if (value is DateTime dt)
                    {
                        logInfo.Contents[key] = dt.ToString(attr.Format);
                    }  else if (value is DateTimeOffset dto)
                    {
                        logInfo.Contents[key] = dto.ToString(attr.Format);
                    }
                    else
                    {
                        logInfo.Contents[key] = value.ToString();
                    }
                }
                else
                {
                    logInfo.Contents[key] = value?.ToString() ?? string.Empty;
                }
            }
            
            return logInfo;
        }
        
        public virtual SLSLogData ReversalConvert(LogInfo log)
        {
            if (log == null) return this;
            if (log.Contents.TryGetValue("__time__", out string timeStr) && long.TryParse(timeStr, out long time))
            {
                // Time = DateTimeOffset.FromUnixTimeSeconds(time).ToOffset(TimeSpan.FromHours(8));
                Time = DateTimeOffset.FromUnixTimeSeconds(time).ToLocalTime();
            }
            else
            {
                Time = log.Time;
            }
                
            foreach (var field in _fields)
            {
                string key = field.Name;
                if (_fieldNameDict.TryGetValue(field.Name, out var newName))
                    key = newName;
                if (log.Contents.TryGetValue(key, out var value) || 
                    log.Contents.TryGetValue(key.ToLower(), out value))
                {
                    TypeConverter converter = TypeDescriptor.GetConverter(field.FieldType);
                    object convertedValue = converter.ConvertFrom(value);
                    field.SetValue(this, convertedValue);
                }
            }
            return this;
        }

        public void Dispose()
        {
            Clear();
            ReferencePool.Recycle(this);
        }

        protected virtual void Clear()
        {
            
        }
    }
}
