using Yuna.Website.Server.Storage;
using Yuna.Website.Server.Model;

using Yuna.Website.Server.Services.DeviceSkillService;
using Yuna.Website.Server.Storage.Repositories.Device;
using Microsoft.IdentityModel.Tokens;
using System.Text.Json;

namespace Yuna.Website.Server.Services.DeviceService
{
    public class DeviceService : IDeviceService
    {
        private readonly IPropService _propService;
        private readonly IDeviceRepository _deviceRepository;
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly ILogger<DeviceService> _logger;
        public DeviceService(
            IPropService propService,
            IDeviceRepository repository,
            IHttpClientFactory factory,
            ILogger<DeviceService> logger)
        {
            _propService = propService;
            _deviceRepository = repository;
            _httpClientFactory = factory;
            _logger = logger;

        }

        public async Task<Device?> AddProps(IReadOnlyList<Prop> props, long deviceId)
        {
            var device = await _deviceRepository.GetById(deviceId);
            if (device is null) return null;

            await _deviceRepository.AddProps(props, deviceId);

            device.Props.AddRange(props);
            return device;
        }

        public async Task<Dictionary<long, string>?> FetchPropsData(Device device)
        {
            var result = new Dictionary<long, string>();

            if (device.Props.IsNullOrEmpty()) return result;

            var client = _httpClientFactory.CreateClient(device.DeviceUrl);
            try
            {
                _logger.LogTrace("Trying to get data\n " +
                    "from device: {Id}\n" +
                    "via url: {DeviceUrl}", device.Id, device.DeviceUrl);

                var response = await client.GetAsync(device.DeviceUrl);

                _logger.LogTrace("Got response\n " +
                    "from device: {Id}\n" +
                    "via url: {DeviceUrl}", device.Id, device.DeviceUrl);

                var strContent = await response.Content.ReadAsStringAsync();

                result = ParseDeviceJson(device.Props, strContent);
            }

            catch (Exception ex)
            {
                _logger.LogError("Fail:  {Message}", ex.Message);
                return null;
            }

            return result;
        }

        private Dictionary<long, string> ParseDeviceJson(IEnumerable<Prop> props, string jsonStr)
        {
            var result = new Dictionary<long, string>();

            using JsonDocument responseJson = JsonDocument.Parse(jsonStr);

            foreach (var prop in props)
            {
                var propExists = responseJson.RootElement.TryGetProperty(prop.JsonValueName, out var propValue);

                if (!propExists) throw new Exception($"Unable to find jsonValue {prop.JsonValueName}, aborting");

                if(propValue.TryGetDouble(out var value))
                {
                    value = Math.Round(value, 2);
                    result.Add(prop.Id, value.ToString());
                    continue;
                }

                result.Add(prop.Id, propValue.GetBoolean().ToString()!);

                _logger.LogTrace("Obtained value for {JsonValueName}", prop.JsonValueName);
            }

            return result;
        }

        public async Task<Device?> Create(Device device)
        {
            var result = await _deviceRepository.Create(device);
            return result;
        }

        public async Task<Device?> Delete(long id)
        {
            var result = await _deviceRepository.Delete(id);
            return result;
        }

        public async Task<Device?> GetById(long id)
        {
            var result = await _deviceRepository.GetById(id);
            return result;
        }
        public async Task<IReadOnlyList<Device>> GetList()
        {
            var result = await _deviceRepository.GetList();
            return result ?? [];
        }

        public async Task<IReadOnlyList<Device>> GetList(long userId)
        {
            var result = await _deviceRepository.GetList(userId);
            return result ?? [];
        }

        public async Task<Device?> Update(Device device)
        {
            var result = await _deviceRepository.Update(device);
            return result;
        }
    }
}