using Ato.CD.Inbound.Shared;
using Ato.EN.IntegrationServices.CodeGenerationFITR;
using Ato.EN.IntegrationServices.CodeGenerationPSS;
using DataContracts;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using VaTS;

namespace Ato.CD.Inbound.FITR202502
{
    internal class CrossFormValidatorPSS : ICrossFormValidator
    {
        private FITR2025 ParentDocument { get; }
        private PSS2018 ChildDocument { get; }
        public List<ProcessMessageDocument> Response { get; private set; }

        public CrossFormValidatorPSS(FITR2025 report, BusinessDocument childDocument)
        {
            ParentDocument = report;
            ChildDocument = (PSS2018)childDocument.ConsumedReport;
            Response = new List<ProcessMessageDocument>();
        }

        public List<ProcessMessageDocument> ValidateCrossFormRules()
        {
            //Parent form cross form rules
            VRATOFITR434045();
            VRATOFITR434046();
            VRATOFITR434056();
            VRATOFITR434080();

            //Child form cross form rules
            VRATOPSS000035();
            VRATOPSS000036();

            return Response;
        }

        #region VR.ATO.FITR.434045

        /*  VR.ATO.FITR.434045
        Tax withheld amount must not be less than the sum of amounts present in the schedule

        Legacy Rule Format:
        (CountDocument('PSS') = 1) AND ^FITR32 > 0 AND ((^FITR72 + 1) < Sum(^PSS23))

        Data Elements:
        ^FITR32 = FITR:RP:Remuneration.ABNNotQuotedPaymentGross.Amount
        ^FITR72 = FITR:RP:IncomeTax.PayAsYouGoWithholding.CreditForAmountsWithheldTFNNotQuotedAndABNNotQuoted.Amount
        ^PSS23 = PSS:RP.Payer.{PSSeqNum}:lrla.02.00:Remuneration.PaymentToForeignResidentGross.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOFITR434045()
        {
            bool assertion = ChildDocument.RPPayerPSSeqNumContextCollection != null && ParentDocument.FITR32 > 0 &&
                (ParentDocument.FITR72 + 1) < ChildDocument.RPPayerPSSeqNumContextCollection.Sum(x => x.PSS23).GetValueOrDefault();

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.GEN.434045",
                    Severity = ProcessMessageSeverity.Error,
                    Description = "Tax withheld amount must not be less than the sum of amounts present in the schedule",
                    LongDescription = "The 'Credit for tax withheld where ABN/TFN not quoted' amount must equal the sum of the Tax withheld amounts on the Payment Summary schedule",
                    Location = "/tns:FITR/tns:RP/tns:IncomeTaxCalculation/tns:PayAsYouGoWithholdingCreditForAmountsWithheldTFNNotQuotedAndABNNotQuotedA",
                    Parameters = new ProcessMessageParameters() {
                        new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.FITR.434045" },
                        new ProcessMessageParameter() { Name = "FITR32", Value = FITR2025Validator.GetValueOrEmpty(ParentDocument.FITR32) },
                        new ProcessMessageParameter() { Name = "FITR72", Value = FITR2025Validator.GetValueOrEmpty(ParentDocument.FITR72) },
                        new ProcessMessageParameter() { Name = "Sum(PSS23)", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.RPPayerPSSeqNumContextCollection.Sum(x => x.PSS23)) }
                    },
                    DocumentSequence = "parent_doc"
                };

                Response.Add(processMessage);
            }
        }

        #endregion

        #region VR.ATO.FITR.434046

        /*  VR.ATO.FITR.434046
        Gross payment amount must equal the sum of amounts in the Payment Summary schedule

        Legacy Rule Format:
        IF (COUNT(SCHEDULE = "PSS") = 1) AND SUM([PSS19]) <> [FITR32]

        Data Elements:
        ^FITR32 = FITR:RP:Remuneration.ABNNotQuotedPaymentGross.Amount
        ^PSS19 = PSS:RP.Payer.{PSSeqNum}:lrla.02.00:Remuneration.ABNNotQuotedPaymentGross.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOFITR434046()
        {
            bool assertion = ChildDocument.RPPayerPSSeqNumContextCollection != null &&
                ChildDocument.RPPayerPSSeqNumContextCollection.Sum(x => x.PSS19).GetValueOrDefault() != ParentDocument.FITR32.GetValueOrDefault();

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument
                {
                    Code = "CMN.ATO.GEN.434046",
                    Severity = ProcessMessageSeverity.Error,
                    Description = "Gross payment amount must equal the sum of amounts in the Payment Summary schedule",
                    Location = "/tns:FITR/tns:RP/tns:Income/tns:RemunerationABNNotQuotedPaymentGrossA",
                    Parameters = new ProcessMessageParameters {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.FITR.434046" },
                        new ProcessMessageParameter { Name = "FITR32", Value = FITR2025Validator.GetValueOrEmpty(ParentDocument.FITR32) },
                        new ProcessMessageParameter { Name = "PSS19", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.RPPayerPSSeqNumContextCollection.Sum(x => x.PSS19)) }
                    },
                    DocumentSequence = "parent_doc"
                };

                Response.Add(processMessage);
            }
        }

        #endregion

        #region VR.ATO.FITR.434056

        /*  VR.ATO.FITR.434056
        Other income must not be less than Gross payment amounts on Payment Summary schedule

        Legacy Rule Format:
        (CountDocument('PSS') = 1) AND Sum(^PSS18) > ^FITR47

        Data Elements:
        RP:^FITR47 = FITR:RP:Income.Other.Amount
        RP:^PSS18 = PSS:RP.Payer.{PSSeqNum}:lrla.02.00:Remuneration.PaymentToForeignResidentGross.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOFITR434056()
        {
            bool assertion = ChildDocument.RPPayerPSSeqNumContextCollection != null &&
                ChildDocument.RPPayerPSSeqNumContextCollection.Sum(x => x.PSS18).GetValueOrDefault() > ParentDocument.FITR47.GetValueOrDefault();

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.GEN.434056",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Other income must not be less than Gross payment amounts on Payment Summary schedule",
                    LongDescription = @"The 'Other income' amount must not be less than the sum of all 'Gross payment' amounts on the Payment Summary schedule",
                    Location = "/tns:FITR/tns:RP/tns:Income/tns:OtherA",
                    Parameters = new ProcessMessageParameters() {
                        new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.FITR.434056" },
                        new ProcessMessageParameter() { Name = "FITR47", Value = FITR2025Validator.GetValueOrEmpty(ParentDocument.FITR47) },
                        new ProcessMessageParameter() { Name = "PSS18", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.RPPayerPSSeqNumContextCollection.Sum(x => x.PSS18)) }
                    },
                    DocumentSequence = "parent_doc"
                };

                Response.Add(processMessage);
            }
        }

        #endregion

        #region VR.ATO.FITR.434080

        /*  VR.ATO.FITR.434080
        Credit: FRW (excluding capital gains) must not be less than the total of all FRW amounts in Payment Summary schedule

        Legacy Rule Format:
        (CountDocument('PSS') = 1) AND Sum(^PSS22) > (^FITR71 + 1)

        Data Elements:
        ^FITR71 = FITR:RP:IncomeTax.PayAsYouGoWithholding.CreditForAmountsWithheldFromForeignResidents.Amount
        ^PSS22 = PSS:RP.Payer.{PSSeqNum}:rvctc2.02.14:IncomeTax.PayAsYouGoWithholding.CreditTaxWithheldBusinessForeignResident.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOFITR434080()
        {
            bool assertion = ChildDocument.RPPayerPSSeqNumContextCollection != null &&
                ChildDocument.RPPayerPSSeqNumContextCollection.Sum(x => x.PSS22).GetValueOrDefault() > (ParentDocument.FITR71 + 1);

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument
                {
                    Code = "CMN.ATO.GEN.438069",
                    Severity = ProcessMessageSeverity.Error,
                    Description = "Credit: FRW (excluding capital gains) must not be less than the total of all FRW amounts in Payment Summary schedule",
                    LongDescription = "The 'Credit: foreign resident withholding (excluding capital gains)' amount on the main form must not be less than the sum of all 'Foreign resident withholding' amounts on the Payment Summary schedule",
                    Location = "/tns:FITR/tns:RP/tns:IncomeTaxCalculation/tns:PayAsYouGoWithholdingCreditForAmountsWithheldFromForeignResidentsA",
                    Parameters = new ProcessMessageParameters {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.FITR.434080" },
                        new ProcessMessageParameter { Name = "FITR71", Value = FITR2025Validator.GetValueOrEmpty(ParentDocument.FITR71) },
                        new ProcessMessageParameter { Name = "PSS22", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.RPPayerPSSeqNumContextCollection.Sum(x => x.PSS22)) }
                    },
                    DocumentSequence = "parent_doc"
                };

                Response.Add(processMessage);
            }
        }

        #endregion

        #region VR.ATO.PSS.000035

        /*  VR.ATO.PSS.000035
        Payment Type not applicable to Non-individual income tax return

        Legacy Rule Format:
        IF PARENT RETURN <> “IITR” AND ([PSS20] <> NULLORBLANK OR [PSS21] <> NULLORBLANK OR [PSS31] <> NULLORBLANK OR [PSS24] <> NULLORBLANK OR [PSS25] <> NULLORBLANK OR [PSS32] <> NULLORBLANK)
            RETURN VALIDATION MESSAGE
        END IF
            
        Data Elements:
        ^PSS20 = PSS:RP.Payer.{PSSeqNum}:tns:Remuneration.VoluntaryAgreementGross.Amount
        ^PSS21 = PSS:RP.Payer.{PSSeqNum}:tns:Remuneration.LabourHireArrangementPaymentGross.Amount
        ^PSS24 = PSS:RP.Payer.{PSSeqNum}:tns:IncomeTax.PayAsYouGoWithholding.CreditTaxWithheldBusinessVoluntaryAgreement.Amount
        ^PSS25 = PSS:RP.Payer.{PSSeqNum}:tns:IncomeTax.PayAsYouGoWithholding.CreditTaxWithheldBusinessLabourHireOrOtherPayments.Amount
        ^PSS31 = PSS:RP.Payer.{PSSeqNum}:tns:Remuneration.PersonalServicesIncome.AttributedGross.Amount
        ^PSS32 = PSS:RP.Payer.{PSSeqNum}:tns:IncomeTax.PayAsYouGoWithholding.CreditTaxWithheldPersonalServicesIncome.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOPSS000035()
        {
            if (ChildDocument.RPPayerPSSeqNumContextCollection != null)
            {
                IEnumerable<PSS2018.RPPayerPSSeqNumContext> contexts = ChildDocument.RPPayerPSSeqNumContextCollection.Where(
                    p => p.PSS20.HasValue || p.PSS21.HasValue || p.PSS24.HasValue || p.PSS25.HasValue || p.PSS31.HasValue || p.PSS32.HasValue
                );

                IEnumerable<ProcessMessageDocument> processMessages = contexts.Select(p =>
                    new ProcessMessageDocument()
                    {
                        Code = "CMN.ATO.PSS.000035",
                        Severity = ProcessMessageSeverity.Error,
                        Description = "Payment Type not applicable to Non-individual income tax return",
                        Location = $"/xbrli:xbrl/tns:Remuneration.PaymentToForeignResidentGross.Amount[contextRef=\"{p.Id}\"]",
                        Parameters = new ProcessMessageParameters {
                            new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.PSS.000035" },
                            new ProcessMessageParameter { Name = "PSS20", Value = FITR2025Validator.GetValueOrEmpty(p.PSS20)},
                            new ProcessMessageParameter { Name = "PSS21", Value = FITR2025Validator.GetValueOrEmpty(p.PSS21)},
                            new ProcessMessageParameter { Name = "PSS24", Value = FITR2025Validator.GetValueOrEmpty(p.PSS24)},
                            new ProcessMessageParameter { Name = "PSS25", Value = FITR2025Validator.GetValueOrEmpty(p.PSS25)},
                            new ProcessMessageParameter { Name = "PSS31", Value = FITR2025Validator.GetValueOrEmpty(p.PSS31)},
                            new ProcessMessageParameter { Name = "PSS32", Value = FITR2025Validator.GetValueOrEmpty(p.PSS32)}
                        }
                    }
                );

                Response.AddRange(processMessages);
            }
        }

        #endregion // VR.ATO.PSS.000035

        #region VR.ATO.PSS.000036

        /*  VR.ATO.PSS.000036
        Your supplied TFN does not match the TFN supplied on the form it was submitted with

        Legacy Rule Format:
        WHERE IN CONTEXT (RP.Payer.{PSSeqNum)
            IF (RP.Payer.{PSSeqNum}.entity.identifier.TFN <> PARENT RETURN:RP.entity.identifier.TFN)
        RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:
        ^FITR5 = FITR:RP:Identifiers.TaxFileNumber.Identifier
        ^PSS TFN = PSS:RP: Identifiers.TaxFileNumber.Identifier
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOPSS000036()
        {
            if (ChildDocument.RPPayerPSSeqNumContextCollection != null)
            {
                IEnumerable<PSS2018.RPPayerPSSeqNumContext> contexts = ChildDocument.RPPayerPSSeqNumContextCollection.Where(
                    p => p.IdentifierTFN != ParentDocument.FITR5
                );

                IEnumerable<ProcessMessageDocument> processMessages = contexts.Select((p, i) => new { RPPayer = p, Index = i }).Select(p =>
                    new ProcessMessageDocument()
                    {
                        Code = "CMN.ATO.PSS.000036",
                        Severity = ProcessMessageSeverity.Error,
                        Description = "Your supplied TFN does not match the TFN supplied on the form it was submitted with",
                        Location = p.Index == 0 ? "xbrli:xbrl/xbrli:context/xbrli:entity/xbrli:identifier" : $"xbrli:xbrl/xbrli:context[{p.Index + 1}]/xbrli:entity/xbrli:identifier",
                        Parameters = new ProcessMessageParameters { 
                            new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.PSS.000036" },
                            new ProcessMessageParameter { Name = "FITR5", Value = FITR2025Validator.GetValueOrEmpty(ParentDocument.FITR5)},
                            new ProcessMessageParameter { Name = "PSS TFN", Value = FITR2025Validator.GetValueOrEmpty(p.RPPayer.IdentifierTFN )}
                        }
                    }
                );

                Response.AddRange(processMessages);
            }
        }

        #endregion // VR.ATO.PSS.000036
    }
}