using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using EonaCat.Json;
using EonaCat.Logger.Servers.Splunk.Models;

// 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.

namespace EonaCat.Logger.Servers.Splunk
{
    /// <summary>
    /// Splunk Server.
    /// </summary>
    public class Splunk : IDisposable
    {
        private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
        private HttpClient _httpClient;
        private HttpClientHandler _httpClientHandler;
        private bool _disposed;

        public event EventHandler<Exception> OnException;

        public string SplunkHecToken { get; set; } = "splunk-hec-token";
        public string Nickname { get; set; }
        public List<ELogType> TypesToLog { get; set; }

        public string SplunkHecUrl
        {
            get => _splunkHecUrl;
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentNullException(nameof(SplunkHecUrl));
                }

                _splunkHecUrl = value.StartsWith("http", StringComparison.OrdinalIgnoreCase)
                    ? value
                    : $"http://{value}";

                RecreateHttpClient();
            }
        }

        public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token";
        public bool HasHecUrl => !string.IsNullOrWhiteSpace(_splunkHecUrl);
        public bool IsHttpsHecUrl => _splunkHecUrl.StartsWith("https", StringComparison.OrdinalIgnoreCase);
        public bool IsLocalHost => _splunkHecUrl.Contains("127.0.0.1") || _splunkHecUrl.Contains("localhost");

        public Splunk(string splunkHecUrl, string splunkHecToken, HttpClientHandler handler = null, string nickName = null, List<ELogType> typesToLog = null)
        {
            SplunkHecToken = splunkHecToken ?? throw new ArgumentNullException(nameof(splunkHecToken));
            SplunkHecUrl = splunkHecUrl ?? throw new ArgumentNullException(nameof(splunkHecUrl));
            Nickname = nickName ?? $"{splunkHecUrl}_{splunkHecToken}";
            TypesToLog = typesToLog;

            _httpClientHandler = handler ?? new HttpClientHandler();
            CreateHttpClient();
        }

        private void CreateHttpClient()
        {
            DisposeHttpClient();

            _httpClientHandler ??= new HttpClientHandler();
            _httpClient = new HttpClient(_httpClientHandler)
            {
                BaseAddress = new Uri(SplunkHecUrl)
            };

            if (!_httpClient.DefaultRequestHeaders.Contains("Authorization"))
            {
                _httpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}");
            }
        }

        private void RecreateHttpClient()
        {
            DisposeHttpClient();
            _httpClientHandler = new HttpClientHandler();
            CreateHttpClient();
        }

        public void DisableSSLValidation()
        {
            DisposeHttpClient();
            _httpClientHandler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (_, _, _, _) => true
            };
            CreateHttpClient();
        }

        public async Task<HttpResponseMessage> SendAsync(SplunkPayload payload, bool disableSplunkSSL = false)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(Splunk));
            }

            try
            {
                if (payload == null || !HasHecToken || !HasHecUrl)
                {
                    return null;
                }

                if (disableSplunkSSL)
                {
                    DisableSSLValidation();
                }

                _httpClient ??= new HttpClient(_httpClientHandler ?? new HttpClientHandler())
                {
                    BaseAddress = new Uri(SplunkHecUrl)
                };

                var eventObject = new
                {
                    @event = payload.EventData,
                    sourcetype = payload.SourceType,
                    host = payload.Host
                };

                var json = JsonHelper.ToJson(eventObject);

                using var request = new HttpRequestMessage(HttpMethod.Post, "services/collector/event")
                {
                    Content = new StringContent(json, Encoding.UTF8, "application/json")
                };

                return await _httpClient.SendAsync(request).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                OnException?.Invoke(this, ex);
                return null;
            }
        }

        internal void DisposeHttpClient()
        {
            try
            {
                _httpClient?.Dispose();
                _httpClientHandler?.Dispose();
            }
            catch
            {
                // Silent fail
            }
            finally
            {
                _httpClient = null;
                _httpClientHandler = null;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;

            if (disposing)
            {
                DisposeHttpClient();
            }
        }

        ~Splunk()
        {
            Dispose(false);
        }
    }
}
