using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Edge.Core.UniversalApi.Auditing
{
    public class AuditLogInfo
    {
        private static JsonSerializerOptions jsonSerializerOptions;
        static AuditLogInfo()
        {
            jsonSerializerOptions = new JsonSerializerOptions()
            {
                WriteIndented = true,
                PropertyNameCaseInsensitive = true,
            };
            jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
        }
        /// <summary>
        /// UniversalApi must targeting on a Processor's Endpoint, that is DeviceHanlder or App.
        /// </summary>
        public string DeviceHandlerOrAppName { get; set; }

        /// <summary>
        ///  The time when this audit log object has been created.
        /// </summary>
        public DateTime ExecutionTime { get; set; }

        /// <summary>
        /// Total execution duration of the request, in milliseconds. This can be used to observe the performance of the application.
        /// </summary>
        public long ExecutionDuration { get; set; }

        /// <summary>
        /// Id of the current client, for http request, it would be remote client Ip+port.
        /// for mqtt request, it would be mqtt connect user name.
        /// </summary>
        public string ClientIdentity { get; set; }

        /// <summary>
        /// An audit log object may contain zero or more exception. In this way, you can get a report of the failed requests.
        /// </summary>
        public List<Exception> Exceptions { get; }

        public List<AuditLogActionInfo> Actions { get; }

        public AuditLogInfo()
        {
            Actions = new List<AuditLogActionInfo>();
            Exceptions = new List<Exception>();
        }

        private Stopwatch stopwatch = null;
        public void Prepare()
        {
            this.stopwatch = Stopwatch.StartNew();
        }

        public void CommitWithExeResult(object executeResult)
        {
            if (this.Actions.FirstOrDefault() != null && executeResult != null)
            {
                var jsResult = JsonSerializer.Serialize(executeResult, jsonSerializerOptions);
                this.Actions.First().OutputResults = jsResult;
            }

            this.ExecutionDuration = this.stopwatch.ElapsedMilliseconds;
            this.stopwatch.Stop();
        }

        public void CommitWithExceptions(IEnumerable<Exception> exceptions)
        {
            this.ExecutionDuration = this.stopwatch.ElapsedMilliseconds;
            this.stopwatch.Stop();
            this.Exceptions.AddRange(exceptions);
        }

        public override string ToString()
        {
            var sb = new StringBuilder();

            sb.AppendLine($"AUDIT LOG: [{(DeviceHandlerOrAppName ?? "---")} - {(Actions.FirstOrDefault()?.ApiName ?? "-------").PadRight(7)}]");
            sb.AppendLine($"- ClientIdentity   : {ClientIdentity}");
            sb.AppendLine($"- ExecutionTime    : {ExecutionTime.ToString("yyyy-MM-dd HH:mm:ss fff")}");
            sb.AppendLine($"- ExecutionDuration: {ExecutionDuration}");

            if (Actions != null)
            {
                sb.AppendLine("- Actions:");
                foreach (var action in Actions)
                {
                    sb.AppendLine($"  - {action.ApiName}");
                    sb.AppendLine($"    Input : {action.InputParameters}");
                    sb.AppendLine($"    Output: {action.OutputResults}");
                }
            }

            if (Exceptions.Any())
            {
                sb.AppendLine("- Exceptions:");
                foreach (var exception in Exceptions)
                {
                    sb.AppendLine($"  - {exception.Message}");
                    sb.AppendLine($"    {exception}");
                }
            }

            return sb.ToString();
        }
    }

    public class AuditLogActionInfo
    {
        public string ApiName { get; set; }

        public string InputParameters { get; set; }

        public string OutputResults { get; set; }
    }
}
