// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Iot.Operations.Connector;
using Azure.Iot.Operations.Connector.Files;
using Azure.Iot.Operations.Services.AssetAndDeviceRegistry.Models;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

namespace RestThermostatConnector
{
    internal class ThermostatStatusDatasetSampler : IDatasetSampler, IAsyncDisposable
    {
        private readonly HttpClient _httpClient;
        private readonly string _assetName;
        private readonly EndpointCredentials? _credentials;

        private readonly static JsonSerializerOptions _jsonSerializerOptions = new()
        {
            AllowTrailingCommas = true,
        };

        public ThermostatStatusDatasetSampler(HttpClient httpClient, string assetName, EndpointCredentials? credentials)
        {
            _httpClient = httpClient;
            _assetName = assetName;
            _credentials = credentials;
        }

        /// <summary>
        /// Sample the datapoints from the HTTP thermostat and return the full serialized dataset.
        /// </summary>
        /// <param name="dataset">The dataset of an asset to sample.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>The serialized payload containing the sampled dataset.</returns>
        public async Task<byte[]> SampleDatasetAsync(AssetDataset dataset, CancellationToken cancellationToken = default)
        {
            int retryCount = 0;
            while (true)
            {
                try
                {
                    string httpServerTemperatureRequestPath = dataset.DataSource!;

                    if (_credentials != null && _credentials.Username != null && _credentials.Password != null)
                    {
                        // Note that this sample uses username + password for authenticating the connection to the asset. In general,
                        // x509 authentication should be used instead (if available) as it is more secure.
                        string httpServerUsername = _credentials.Username;
                        string httpServerPassword = _credentials.Password;
                        var byteArray = Encoding.ASCII.GetBytes($"{httpServerUsername}:{httpServerPassword}");
                        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
                    }

                    var currentTemperatureStatusHttpResponse = await _httpClient.GetAsync(httpServerTemperatureRequestPath, cancellationToken);

                    if (currentTemperatureStatusHttpResponse.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                    {
                        throw new Exception("Failed to authorize request to HTTP server. Check credentials configured in rest-server-device-definition.yaml.");
                    }

                    currentTemperatureStatusHttpResponse.EnsureSuccessStatusCode();

                    var payloadAsBytes = await currentTemperatureStatusHttpResponse.Content.ReadAsByteArrayAsync(cancellationToken);
                    RestThermostatStatus currentStatus = JsonSerializer.Deserialize<RestThermostatStatus>(payloadAsBytes, _jsonSerializerOptions)!;

                    // The HTTP response payload matches the expected message schema, so return it as-is
                    return payloadAsBytes;
                }
                catch (Exception ex)
                {
                    if (++retryCount >= 3)
                    {
                        throw new InvalidOperationException($"Failed to sample dataset with name {dataset.Name} in asset with name {_assetName}. Error: {ex.Message}", ex);
                    }
                    await Task.Delay(1000, cancellationToken);
                }
            }
        }

        public Task<TimeSpan> GetSamplingIntervalAsync(AssetDataset dataset, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(TimeSpan.FromSeconds(3));
        }

        public ValueTask DisposeAsync()
        {
            _httpClient.Dispose();
            return ValueTask.CompletedTask;
        }
    }
}
