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

namespace Ato.CD.Inbound.SMSFAR
{
    internal class CrossFormValidatorPSS : ICrossFormValidator
    {
        private SMSFAR2026 ParentDocument { get; }

        private PSS2018 ChildDocument { get; }

        internal CrossFormValidatorPSS(SMSFAR2026 report, BusinessDocument childDocument)
        {
            ParentDocument = report;
            ChildDocument = (PSS2018)childDocument.ConsumedReport;
        }

        public List<ProcessMessageDocument> ValidateCrossFormRules()
        {
            var response = new List<ProcessMessageDocument>();

            VRATOPSS000035(response);
            VRATOPSS000036(response);
            VRATOSMSFAR436052(response);
            VRATOSMSFAR436053(response);
            VRATOSMSFAR436072(response);
            VRATOSMSFAR436118(response);

            return response;
        }

        #region VR.ATO.PSS.000035
        /*  VR.ATO.PSS.000035
            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
              ENDIF

              [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   
       */
        private void VRATOPSS000035(List<ProcessMessageDocument> response)
        {
            if (ChildDocument.RPPayerPSSeqNumContextCollection != null)
                response.AddRange(ChildDocument.RPPayerPSSeqNumContextCollection
                    .Where(p => p.PSS20.HasValue || p.PSS21.HasValue || p.PSS24.HasValue || p.PSS25.HasValue ||
                                p.PSS31.HasValue || p.PSS32.HasValue).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"} }
                        }
                    ));
        }
        #endregion

        #region VR.ATO.PSS.000036
        /*  VR.ATO.PSS.000036
            WHERE IN CONTEXT (RP.Payer.{PSSeqNum)
            IF (RP.Payer.{PSSeqNum}.entity.identifier.TFN <> PARENT RETURN:RP.entity.identifier.TFN)
               RETURN VALIDATION MESSAGE
            ENDIF
       */
        private void VRATOPSS000036(List<ProcessMessageDocument> response)
        {
            if (ChildDocument.RPPayerPSSeqNumContextCollection != null)
                response.AddRange(ChildDocument.RPPayerPSSeqNumContextCollection
                    .Where(p => p.IdentifierTFN != ParentDocument.RPIdentifierTFN)
                    .Select((p, i) => new {RPPayer = p, Idx = 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.Idx == 0 ? "xbrli:xbrl/xbrli:context/xbrli:entity/xbrli:identifier" : $"xbrli:xbrl/xbrli:context[{p.Idx + 1}]/xbrli:entity/xbrli:identifier",
                        Parameters = new ProcessMessageParameters { new ProcessMessageParameter {Name = "RuleIdentifier", Value = "VR.ATO.PSS.000036"} }
                    }));
        }
        #endregion

        #region VR.ATO.SMSFAR.436052
        /*  VR.ATO.SMSFAR.436052
            Gross payment amount must equal the sum of amounts in the Payment Summary schedule

            Legacy Rule Format:
            IF (COUNT(SCHEDULE = "PSS") = 1) AND (SUM(ALL OCCURRENCES OF([PSS19]))) <> [SMSFAR42] 
                RETURN VALIDATION MESSAGE
            ENDIF
                    
            [PSS19] = PSS:RP.Payer.{PSSeqNum}:tns:Remuneration.ABNNotQuotedPaymentGross.Amount
            [SMSFAR42] = SMSFAR2019:RP:tns:Remuneration.ABNNotQuotedPaymentGross.Amount

            Technical Business Rule Format:
            (CountDocument('PSS') = 1) AND (Sum(^PSS19)) <> ^SMSFAR42

            Data Elements:
            
            RP:^SMSFAR42 = tns:Remuneration.ABNNotQuotedPaymentGross.Amount
            
            RP:^PSS19 = PSS:RP.Payer.{PSSeqNum}:tns:Remuneration.ABNNotQuotedPaymentGross.Amount
        */
        private void VRATOSMSFAR436052(List<ProcessMessageDocument> response)
        {
            bool assertion = ChildDocument.RPPayerPSSeqNumContextCollection != null &&
                             ChildDocument.RPPayerPSSeqNumContextCollection.ToList().Sum(p => p.PSS19) !=
                             ParentDocument.SMSFAR42.GetValueOrDefault();
            if (assertion)
            {
                response.Add(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 = "/xbrli:xbrl/tns:Remuneration.ABNNotQuotedPaymentGross.Amount",
                    Parameters = new ProcessMessageParameters
                    {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.SMSFAR.436052" },
                        new ProcessMessageParameter { Name = "SMSFAR42", Value = ParentDocument.SMSFAR42.GetValueOrDefault().ToString() }
                    },
                    DocumentSequence = "parent_doc"
                });
            }
        }
        #endregion

        #region VR.ATO.SMSFAR.436053
        /*  VR.ATO.SMSFAR.436053
            The 'Credit for tax withheld - where TFN or ABN not quoted' amount must equal the sum of the Tax withheld amounts on the Payment Summary schedule

            Legacy Rule Format:
            IF COUNT(SCHEDULE = "PSS") = 1 AND [SMSFAR42] > 0 AND [SMSFAR80] + 1 < SUM(ALL OCCURRENCES OF([PSS23]))
                RETURN VALIDATION MESSAGE
            ENDIF
                    
            [PSS23] = PSS:RP.Payer.{PSSeqNum}:tns:IncomeTax.PayAsYouGoWithholding.CreditTaxWithheldBusinessABNNotQuoted.Amount
            [SMSFAR42] = SMSFAR2019:RP:tns:Remuneration.ABNNotQuotedPaymentGross.Amount
            [SMSFAR80] = SMSFAR2019:RP:tns:IncomeTax.PayAsYouGoWithholding.CreditForAmountsWithheldTFNNotQuotedAndABNNotQuoted.Amount

            Technical Business Rule Format:
            CountDocument('PSS') = 1 AND ^SMSFAR42 > 0 AND ^SMSFAR80 + 1 < Sum(^PSS23)

            Data Elements:
            
            RP:^SMSFAR42 = tns:Remuneration.ABNNotQuotedPaymentGross.Amount
            
            RP:^PSS23 = PSS:RP.Payer.{PSSeqNum}:tns:IncomeTax.PayAsYouGoWithholding.CreditTaxWithheldBusinessABNNotQuoted.Amount
            
            RP:^SMSFAR80 = tns:IncomeTax.PayAsYouGoWithholding.CreditForAmountsWithheldTFNNotQuotedAndABNNotQuoted.Amount
        */
        private void VRATOSMSFAR436053(List<ProcessMessageDocument> response)
        {
            bool assertion = ChildDocument.RPPayerPSSeqNumContextCollection != null &&
                             ParentDocument.SMSFAR42.GetValueOrDefault() > 0 &&
                             ParentDocument.SMSFAR80.GetValueOrDefault() + 1 < ChildDocument
                                 .RPPayerPSSeqNumContextCollection.ToList().Sum(p => p.PSS23);
            if (assertion)
            {
                response.Add(new ProcessMessageDocument
                {
                    Code = "CMN.ATO.SMSFAR.436334",
                    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 TFN or ABN not quoted' amount must not be less than the sum of the Tax withheld amounts on the Payment Summary schedule",
                    Location = "/xbrli:xbrl/tns:Remuneration.ABNNotQuotedPaymentGross.Amount",
                    Parameters = new ProcessMessageParameters
                    {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.SMSFAR.436053" },
                        new ProcessMessageParameter { Name = "SMSFAR42", Value = ParentDocument.SMSFAR42.GetValueOrDefault().ToString() },
                        new ProcessMessageParameter { Name = "SMSFAR80", Value = ParentDocument.SMSFAR80.GetValueOrDefault().ToString() }
                    },
                    DocumentSequence = "parent_doc"
                });
            }
        }
        #endregion

        #region VR.ATO.SMSFAR.436072
        /*  VR.ATO.SMSFAR.436072
            The 'Other income' amount must not be less than the sum of all 'Gross payment' amounts on the Payment Summary schedule

            Legacy Rule Format:
            IF (COUNT(SCHEDULE = "PSS") = 1) AND (SUM(ALL OCCURRENCES OF([PSS18])) > [SMSFAR53])
                RETURN VALIDATION MESSAGE
            ENDIF
                    
            [PSS18] = PSS:RP.Payer.{PSSeqNum}:tns:Remuneration.PaymentToForeignResidentGross.Amount
            [SMSFAR53] = SMSFAR2019:RP:tns:Income.Other.Amount

            Technical Business Rule Format:
            (CountDocument('PSS') = 1) AND (Sum(^PSS18) > ^SMSFAR53)

            Data Elements:
            
            RP:^SMSFAR53 = tns:Income.Other.Amount
            
            RP:^PSS18 = PSS:RP.Payer.{PSSeqNum}:tns:Remuneration.PaymentToForeignResidentGross.Amount
        */
        private void VRATOSMSFAR436072(List<ProcessMessageDocument> response)
        {
            bool assertion = ChildDocument.RPPayerPSSeqNumContextCollection != null &&
                             ChildDocument.RPPayerPSSeqNumContextCollection.ToList().Sum(p => p.PSS18) >
                             ParentDocument.SMSFAR53.GetValueOrDefault();
            if (assertion)
            {
                response.Add(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 = "/xbrli:xbrl/tns:Income.Other.Amount",
                    Parameters = new ProcessMessageParameters
                    {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.SMSFAR.436072" },
                        new ProcessMessageParameter { Name = "SMSFAR53", Value = ParentDocument.SMSFAR53.GetValueOrDefault().ToString() }
                    },
                    DocumentSequence = "parent_doc"
                });
            }
        }
        #endregion

        #region VR.ATO.SMSFAR.436118
        /*  VR.ATO.SMSFAR.436118
            The 'Credit for tax withheld - foreign resident withholding (excluding capital gains)' amount on the main form must not be less than the sum of all 'Foreign resident withholding (excluding capital gains)' amounts on the Payment Summary schedule.

            Legacy Rule Format:
            IF (COUNT(SCHEDULE = "PSS") = 1) AND (SUM(ALL OCCURRENCES OF([PSS22]))) > ([SMSFAR79] + 1)  
                RETURN VALIDATION MESSAGE
            ENDIF
                    
            [PSS22] = PSS:RP.Payer.{PSSeqNum}:tns:IncomeTax.PayAsYouGoWithholding.CreditTaxWithheldBusinessForeignResident.Amount
            [SMSFAR79] = SMSFAR2019:RP:tns:IncomeTax.PayAsYouGoWithholding.CreditForAmountsWithheldFromForeignResidents.Amount

            Technical Business Rule Format:
            (CountDocument('PSS') = 1) AND (Sum(^PSS22)) > (^SMSFAR79 + 1)

            Data Elements:
            
            RP:^SMSFAR79 = tns:IncomeTax.PayAsYouGoWithholding.CreditForAmountsWithheldFromForeignResidents.Amount
            
            RP:^PSS22 = PSS:RP.Payer.{PSSeqNum}:tns:IncomeTax.PayAsYouGoWithholding.CreditTaxWithheldBusinessForeignResident.Amount
        */
        private void VRATOSMSFAR436118(List<ProcessMessageDocument> response)
        {
            bool assertion = ChildDocument.RPPayerPSSeqNumContextCollection != null &&
                             ChildDocument.RPPayerPSSeqNumContextCollection.ToList().Sum(p => p.PSS22) >
                             ParentDocument.SMSFAR79.GetValueOrDefault() + 1;
            if (assertion)
            {
                response.Add(new ProcessMessageDocument
                {
                    Code = "CMN.ATO.SMSFAR.437137",
                    Severity = ProcessMessageSeverity.Error,
                    Description = "Credit for tax withheld - foreign resident withholding (excluding capital gains) amount must not be less than the total amount on the schedule.",
                    LongDescription = "The 'Credit for tax withheld - foreign resident withholding (excluding capital gains)' amount on the main form must not be less than the sum of all 'Foreign resident withholding (excluding capital gains)' amounts on the Payment Summary schedule.",
                    Location = "/xbrli:xbrl/tns:IncomeTax.PayAsYouGoWithholding.CreditForAmountsWithheldFromForeignResidents.Amount",
                    Parameters = new ProcessMessageParameters
                    {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.SMSFAR.436118" },
                        new ProcessMessageParameter { Name = "SMSFAR79", Value = ParentDocument.SMSFAR79.GetValueOrDefault().ToString() }
                    },
                    DocumentSequence = "parent_doc"
                });
            }
        }
        #endregion
    }
}