using Hcs.ClientNet.Api.Payload.DeviceMetering;
using Hcs.ClientNet.Internal;
using Hcs.Service.Async.DeviceMetering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Hcs.ClientNet.Api.Request.DeviceMetering
{
    internal class ExportMeteringDeviceHistoryRequest(ClientBase client) : DeviceMeteringRequestBase(client)
    {
        protected override bool EnableMinimalResponseWaitDelay => false;

        internal async Task<IEnumerable<exportMeteringDeviceHistoryResultType>> ExecuteAsync(ExportMeteringDeviceHistoryPayload payload, CancellationToken token)
        {
            ThrowIfPayloadIncorrect(payload);

            var request = GetRequestFromPayload(payload);
            var result = await SendAndWaitResultAsync(request, async asyncClient =>
            {
                var response = await asyncClient.exportMeteringDeviceHistoryAsync(CreateRequestHeader(), request);
                return response.AckRequest.Ack;
            }, token);

            return result.Items.OfType<exportMeteringDeviceHistoryResultType>();
        }

        private void ThrowIfPayloadIncorrect(ExportMeteringDeviceHistoryPayload payload)
        {
            if (payload.meteringDeviceType?.Length <= 0 && payload.municipalResource?.Length <= 0 &&
                payload.meteringDeviceRootGUID?.Length <= 0)
            {
                throw new ArgumentException($"{nameof(payload.meteringDeviceType)}/{nameof(payload.municipalResource)}/{nameof(payload.meteringDeviceRootGUID)} are empty");
            }

            if (payload.meteringDeviceType?.Length + payload.municipalResource?.Length +
                payload.meteringDeviceRootGUID?.Length > 100)
            {
                throw new ArgumentException($"Too much {nameof(payload.meteringDeviceType)}/{nameof(payload.municipalResource)}/{nameof(payload.meteringDeviceRootGUID)} values");
            }

            if (payload.inputDateFrom.HasValue && payload.inputDateTo.HasValue)
            {
                if (payload.inputDateFrom.HasValue && payload.inputDateTo.HasValue &&
                    payload.inputDateFrom.Value > payload.inputDateTo.Value)
                {
                    throw new ArgumentException($"{nameof(payload.inputDateFrom)} must be earlier than {nameof(payload.inputDateTo)}");
                }

                if (payload.inputDateTo.Value - payload.inputDateFrom.Value > TimeSpan.FromDays(
                    DateTime.DaysInMonth(payload.inputDateTo.Value.Year, payload.inputDateTo.Value.Month) +
                    DateTime.DaysInMonth(payload.inputDateFrom.Value.Year, payload.inputDateFrom.Value.Month)))
                {
                    throw new ArgumentException($"Too big range from {nameof(payload.inputDateFrom)} to {nameof(payload.inputDateTo)}");
                }
            }
            else if (payload.inputDateFrom.HasValue && !payload.inputDateTo.HasValue)
            {
                throw new ArgumentException($"{nameof(payload.inputDateTo)} is null");
            }
            else if (!payload.inputDateFrom.HasValue && payload.inputDateTo.HasValue)
            {
                throw new ArgumentException($"{nameof(payload.inputDateFrom)} is null");
            }
        }

        private exportMeteringDeviceHistoryRequest GetRequestFromPayload(ExportMeteringDeviceHistoryPayload payload)
        {
            var items = new List<object>();
            var itemsElementName = new List<ItemsChoiceType4>();
            if (payload.meteringDeviceType != null)
            {
                foreach (var meteringDeviceType in payload.meteringDeviceType)
                {
                    items.Add(new nsiRef()
                    {
                        Code = meteringDeviceType.Code,
                        GUID = meteringDeviceType.GUID
                    });
                    itemsElementName.Add(ItemsChoiceType4.MeteringDeviceType);
                }
            }
            if (payload.municipalResource != null)
            {
                foreach (var municipalResource in payload.municipalResource)
                {
                    items.Add(new nsiRef()
                    {
                        Code = municipalResource.Code,
                        GUID = municipalResource.GUID
                    });
                    itemsElementName.Add(ItemsChoiceType4.MunicipalResource);
                }
            }
            if (payload.meteringDeviceRootGUID != null)
            {
                foreach (var meteringDeviceRootGUID in payload.meteringDeviceRootGUID)
                {
                    items.Add(meteringDeviceRootGUID);
                    itemsElementName.Add(ItemsChoiceType4.MeteringDeviceRootGUID);
                }
            }

            // http://open-gkh.ru/DeviceMetering/exportMeteringDeviceHistoryRequest.html
            var request = new exportMeteringDeviceHistoryRequest
            {
                Id = Constants.SIGNED_XML_ELEMENT_ID,
                version = "13.1.3.1",
                FIASHouseGuid = payload.fiasHouseGuid,
                Items = [.. items],
                ItemsElementName = [.. itemsElementName]
            };

            if (payload.commissioningDateFrom.HasValue)
            {
                request.CommissioningDateFrom = payload.commissioningDateFrom.Value;
                request.CommissioningDateFromSpecified = true;
            }

            if (payload.сommissioningDateTo.HasValue)
            {
                request.CommissioningDateTo = payload.сommissioningDateTo.Value;
                request.CommissioningDateToSpecified = true;
            }

            if (payload.serchArchived.HasValue)
            {
                request.SerchArchived = payload.serchArchived.Value;
                request.SerchArchivedSpecified = true;
            }

            if (payload.archiveDateFrom.HasValue)
            {
                request.ArchiveDateFrom = payload.archiveDateFrom.Value;
                request.ArchiveDateFromSpecified = true;
            }

            if (payload.archiveDateTo.HasValue)
            {
                request.ArchiveDateTo = payload.archiveDateTo.Value;
                request.ArchiveDateToSpecified = true;
            }

            if (payload.inputDateFrom.HasValue)
            {
                request.inputDateFrom = payload.inputDateFrom.Value;
                request.inputDateFromSpecified = true;
            }

            if (payload.inputDateTo.HasValue)
            {
                request.inputDateTo = payload.inputDateTo.Value;
                request.inputDateToSpecified = true;
            }

            if (payload.excludePersonAsDataSource.HasValue)
            {
                request.ExcludePersonAsDataSource = payload.excludePersonAsDataSource.Value;
                request.ExcludePersonAsDataSourceSpecified = true;
            }

            if (payload.excludeCurrentOrgAsDataSource.HasValue)
            {
                request.ExcludeCurrentOrgAsDataSource = payload.excludeCurrentOrgAsDataSource.Value;
                request.ExcludeCurrentOrgAsDataSourceSpecified = true;
            }

            if (payload.excludeOtherOrgAsDataSource.HasValue)
            {
                request.ExcludeOtherOrgAsDataSource = payload.excludeOtherOrgAsDataSource.Value;
                request.ExcludeOtherOrgAsDataSourceSpecified = true;
            }

            return request;
        }
    }
}
