using EonaCat.Json;
using System;
using System.Dynamic;
using System.IO;
using System.Net;
using System.Text;

// 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.Zabbix.API
{
    /// <summary>
    /// Zabbix API client for authentication and communication with Zabbix server.
    /// </summary>
    public class ZabbixApi : IDisposable
    {
        private readonly string _user;
        private readonly string _password;
        private readonly string _zabbixUrl;
        private readonly string _basicAuth;
        private string _auth;
        private bool _disposed;

        public event EventHandler<Exception> OnException;
        public bool IsLoggedIn { get; private set; }

        public ZabbixApi(string user, string password, string zabbixUrl, bool useBasicAuth = false)
        {
            _user = user ?? throw new ArgumentNullException(nameof(user));
            _password = password ?? throw new ArgumentNullException(nameof(password));
            _zabbixUrl = zabbixUrl ?? throw new ArgumentNullException(nameof(zabbixUrl));

            _basicAuth = useBasicAuth
                ? Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes($"{_user}:{_password}"))
                : null;
        }

        /// <summary>
        /// Login and retrieve authentication token from Zabbix API.
        /// </summary>
        public void Login()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(ZabbixApi));
            }

            dynamic authParams = new ExpandoObject();
            authParams.user = _user;
            authParams.password = _password;

            var response = ResponseAsObject("user.login", authParams);
            _auth = response?.Result;
            IsLoggedIn = !string.IsNullOrEmpty(_auth);
        }

        /// <summary>
        /// Logout from Zabbix API.
        /// </summary>
        public bool Logout()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(ZabbixApi));
            }

            var response = ResponseAsObject("user.logout", Array.Empty<string>());
            return response?.Result ?? false;
        }

        /// <summary>
        /// Send a Zabbix API request and return JSON response as string.
        /// </summary>
        public string ResponseAsJson(string method, object parameters)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(ZabbixApi));
            }

            var request = CreateRequest(method, parameters);
            var jsonParams = JsonHelper.ToJson(request);
            return SendRequest(jsonParams);
        }

        /// <summary>
        /// Send a Zabbix API request and return deserialized response.
        /// </summary>
        public ZabbixApiResponse ResponseAsObject(string method, object parameters)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(ZabbixApi));
            }

            var request = CreateRequest(method, parameters);
            var jsonParams = JsonHelper.ToJson(request);
            var responseJson = SendRequest(jsonParams);
            return JsonHelper.ToObject<ZabbixApiResponse>(responseJson);
        }

        private ZabbixApiRequest CreateRequest(string method, object parameters) =>
            new("2.0", method, 1, _auth, parameters);

        private string SendRequest(string jsonParams)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(ZabbixApi));
            }

            try
            {
                var request = (HttpWebRequest)WebRequest.Create(_zabbixUrl);
                request.Method = "POST";
                request.ContentType = "application/json-rpc";

                if (!string.IsNullOrEmpty(_basicAuth))
                {
                    request.Headers.Add("Authorization", $"Basic {_basicAuth}");
                }

                // Write JSON request
                using (var requestStream = request.GetRequestStream())
                using (var writer = new StreamWriter(requestStream, Encoding.UTF8))
                {
                    writer.Write(jsonParams);
                }

                // Read JSON response
                using var response = (HttpWebResponse)request.GetResponse();
                using var responseStream = response.GetResponseStream();
                if (responseStream == null)
                {
                    return null;
                }

                using var reader = new StreamReader(responseStream, Encoding.UTF8);
                return reader.ReadToEnd();
            }
            catch (Exception ex)
            {
                OnException?.Invoke(this, ex);
                return null;
            }
        }

        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;

            GC.SuppressFinalize(this);
        }

        ~ZabbixApi()
        {
            Dispose();
        }
    }
}
