using Hcs.ClientNet.Api.Payload.Bills;
using Hcs.ClientNet.Api.Request.Exception;
using Hcs.ClientNet.Api.Type;
using Hcs.ClientNet.Internal;
using Hcs.Service.Async.Bills;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Hcs.ClientNet.Api.Request.Bills
{
    internal class ImportPaymentDocumentDataRequest(ClientBase client) : BillsRequestBase(client)
    {
        internal async Task<bool> ExecuteAsync(ImportPaymentDocumentDataPayload payload, CancellationToken token)
        {
            // TODO: Добавить проверку пейлоада

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

            result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
            {
                throw RemoteException.CreateNew(error.ErrorCode, error.Description);
            });

            result.Items.OfType<CommonResultType>().ToList().ForEach(commonResult =>
            {
                commonResult.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
                {
                    throw RemoteException.CreateNew(error.ErrorCode, error.Description);
                });
            });

            return true;
        }

        private importPaymentDocumentRequest GetRequestFromPayload(ImportPaymentDocumentDataPayload payload)
        {
            var items = new List<object>();
            if (payload.confirmAmountsCorrect)
            {
                items.Add(true);
            }
            items.Add(payload.month);
            items.Add(payload.year);

            var paymentInformations = new Dictionary<ImportPaymentDocumentDataPayload.PaymentInformation, importPaymentDocumentRequestPaymentInformation>();
            foreach (var entry in payload.paymentInformation)
            {
                var paymentInformation = new importPaymentDocumentRequestPaymentInformation()
                {
                    TransportGUID = Guid.NewGuid().ToString(),
                    BankBIK = entry.bankBIK,
                    operatingAccountNumber = entry.operatingAccountNumber
                };
                paymentInformations.Add(entry, paymentInformation);

                items.Add(paymentInformation);
            }

            foreach (var entry in payload.paymentDocument)
            {
                var chargeInfo = new List<object>();
                foreach (var subEntry in entry.chargeInfo)
                {
                    if (subEntry is ImportPaymentDocumentDataPayload.MunicipalService municipalService)
                    {
                        var item = new PDServiceChargeTypeMunicipalService()
                        {
                            ServiceType = new nsiRef()
                            {
                                Code = municipalService.serviceType.Code,
                                GUID = municipalService.serviceType.GUID
                            },
                            Rate = municipalService.rate,
                            TotalPayable = municipalService.totalPayable
                        };

                        if (municipalService.moneyRecalculation.HasValue)
                        {
                            item.ServiceCharge = new ServiceChargeImportType()
                            {
                                MoneyRecalculation = municipalService.moneyRecalculation.Value,
                                MoneyRecalculationSpecified = true
                            };
                        }
                        if (municipalService.moneyDiscount.HasValue)
                        {
                            item.ServiceCharge ??= new ServiceChargeImportType();
                            item.ServiceCharge.MoneyDiscount = municipalService.moneyDiscount.Value;
                            item.ServiceCharge.MoneyDiscountSpecified = true;
                        }

                        if (municipalService.houseOverallNeedsNorm.HasValue)
                        {
                            item.ServiceInformation = new ServiceInformation()
                            {
                                houseOverallNeedsNorm = municipalService.houseOverallNeedsNorm.Value,
                                houseOverallNeedsNormSpecified = true
                            };
                        }
                        if (municipalService.individualConsumptionNorm.HasValue)
                        {
                            item.ServiceInformation ??= new ServiceInformation();
                            item.ServiceInformation.individualConsumptionNorm = municipalService.individualConsumptionNorm.Value;
                            item.ServiceInformation.individualConsumptionNormSpecified = true;
                        }
                        if (municipalService.individualConsumptionCurrentValue.HasValue)
                        {
                            item.ServiceInformation ??= new ServiceInformation();
                            item.ServiceInformation.individualConsumptionCurrentValue = municipalService.individualConsumptionCurrentValue.Value;
                            item.ServiceInformation.individualConsumptionCurrentValueSpecified = true;
                        }
                        if (municipalService.houseOverallNeedsCurrentValue.HasValue)
                        {
                            item.ServiceInformation ??= new ServiceInformation();
                            item.ServiceInformation.houseOverallNeedsCurrentValue = municipalService.houseOverallNeedsCurrentValue.Value;
                            item.ServiceInformation.houseOverallNeedsCurrentValueSpecified = true;
                        }
                        if (municipalService.houseTotalIndividualConsumption.HasValue)
                        {
                            item.ServiceInformation ??= new ServiceInformation();
                            item.ServiceInformation.houseTotalIndividualConsumption = municipalService.houseTotalIndividualConsumption.Value;
                            item.ServiceInformation.houseTotalIndividualConsumptionSpecified = true;
                        }
                        if (municipalService.houseTotalHouseOverallNeeds.HasValue)
                        {
                            item.ServiceInformation ??= new ServiceInformation();
                            item.ServiceInformation.houseTotalHouseOverallNeeds = municipalService.houseTotalHouseOverallNeeds.Value;
                            item.ServiceInformation.houseTotalHouseOverallNeedsSpecified = true;
                        }

                        var consumption = new List<PDServiceChargeTypeMunicipalServiceVolume>();
                        if (municipalService.individualConsumptionVolumeDeterminingMethod.HasValue)
                        {
                            consumption.Add(new PDServiceChargeTypeMunicipalServiceVolume()
                            {
                                determiningMethod = municipalService.individualConsumptionVolumeDeterminingMethod.Value.ToServiceType(),
                                determiningMethodSpecified = true,
                                type = PDServiceChargeTypeMunicipalServiceVolumeType.I,
                                typeSpecified = true,
                                Value = municipalService.individualConsumptionVolumeValue.Value
                            });
                        }
                        if (municipalService.overallConsumptionVolumeDeterminingMethod.HasValue)
                        {
                            consumption.Add(new PDServiceChargeTypeMunicipalServiceVolume()
                            {
                                determiningMethod = municipalService.overallConsumptionVolumeDeterminingMethod.Value.ToServiceType(),
                                determiningMethodSpecified = true,
                                type = PDServiceChargeTypeMunicipalServiceVolumeType.O,
                                typeSpecified = true,
                                Value = municipalService.overallConsumptionVolumeValue.Value
                            });
                        }
                        item.Consumption = [.. consumption];

                        if (municipalService.multiplyingFactorRatio.HasValue)
                        {
                            item.MultiplyingFactor = new PDServiceChargeTypeMunicipalServiceMultiplyingFactor()
                            {
                                Ratio = municipalService.multiplyingFactorRatio.Value
                            };

                            if (municipalService.amountOfExcessFees.HasValue)
                            {
                                item.MultiplyingFactor.AmountOfExcessFees = municipalService.amountOfExcessFees.Value;
                                item.MultiplyingFactor.AmountOfExcessFeesSpecified = true;
                            }
                        }

                        if (municipalService.municipalServiceIndividualConsumptionPayable.HasValue)
                        {
                            item.MunicipalServiceIndividualConsumptionPayable = municipalService.municipalServiceIndividualConsumptionPayable.Value;
                            item.MunicipalServiceIndividualConsumptionPayableSpecified = true;
                        }

                        if (municipalService.municipalServiceCommunalConsumptionPayable.HasValue)
                        {
                            item.MunicipalServiceCommunalConsumptionPayable = municipalService.municipalServiceCommunalConsumptionPayable.Value;
                            item.MunicipalServiceCommunalConsumptionPayableSpecified = true;
                        }

                        if (municipalService.amountOfPaymentMunicipalServiceIndividualConsumption.HasValue)
                        {
                            item.AmountOfPaymentMunicipalServiceIndividualConsumption = municipalService.amountOfPaymentMunicipalServiceIndividualConsumption.Value;
                            item.AmountOfPaymentMunicipalServiceIndividualConsumptionSpecified = true;
                        }

                        if (municipalService.amountOfPaymentMunicipalServiceCommunalConsumption.HasValue)
                        {
                            item.AmountOfPaymentMunicipalServiceCommunalConsumption = municipalService.amountOfPaymentMunicipalServiceCommunalConsumption.Value;
                            item.AmountOfPaymentMunicipalServiceCommunalConsumptionSpecified = true;
                        }

                        if (municipalService.accountingPeriodTotal.HasValue)
                        {
                            item.AccountingPeriodTotal = municipalService.accountingPeriodTotal.Value;
                            item.AccountingPeriodTotalSpecified = true;
                        }

                        chargeInfo.Add(new PaymentDocumentTypeChargeInfo()
                        {
                            Item = item
                        });
                    }

                    // TODO: Обработать ошибку
                }

                var paymentDocument = new importPaymentDocumentRequestPaymentDocument()
                {
                    TransportGUID = Guid.NewGuid().ToString(),
                    Items1 = [paymentInformations[entry.paymentInformation].TransportGUID],
                    AccountGuid = entry.accountGuid,
                    PaymentDocumentNumber = entry.paymentDocumentNumber,
                    Items = [.. chargeInfo],
                    Item = true,
                    ItemElementName = entry.exposeNotWithdraw ? ItemChoiceType5.Expose : ItemChoiceType5.Withdraw
                };

                if (entry.debtPreviousPeriods.HasValue)
                {
                    paymentDocument.DebtPreviousPeriods = entry.debtPreviousPeriods.Value;
                    paymentDocument.DebtPreviousPeriodsSpecified = true;
                }

                if (entry.advanceBllingPeriod.HasValue)
                {
                    paymentDocument.AdvanceBllingPeriod = entry.advanceBllingPeriod.Value;
                    paymentDocument.AdvanceBllingPeriodSpecified = true;
                }

                if (entry.totalPayableByPDWithDebtAndAdvance.HasValue)
                {
                    paymentDocument.TotalPayableByPDWithDebtAndAdvance = entry.totalPayableByPDWithDebtAndAdvance.Value;
                    paymentDocument.TotalPayableByPDWithDebtAndAdvanceSpecified = true;
                }

                if (entry.totalPayableByPD.HasValue)
                {
                    paymentDocument.TotalPayableByPD = entry.totalPayableByPD.Value;
                    paymentDocument.TotalPayableByPDSpecified = true;
                }

                if (entry.paidCash.HasValue)
                {
                    paymentDocument.PaidCash = entry.paidCash.Value;
                    paymentDocument.PaidCashSpecified = true;
                }

                if (entry.dateOfLastReceivedPayment.HasValue)
                {
                    paymentDocument.DateOfLastReceivedPayment = entry.dateOfLastReceivedPayment.Value;
                    paymentDocument.DateOfLastReceivedPaymentSpecified = true;
                }

                items.Add(paymentDocument);
            }

            // http://open-gkh.ru/Bills/importPaymentDocumentRequest.html
            return new importPaymentDocumentRequest
            {
                Id = Constants.SIGNED_XML_ELEMENT_ID,
                version = "11.2.0.16",
                Items = [.. items]
            };
        }
    }
}
