using Ato.CD.Inbound.Shared;
using Ato.EN.IntegrationServices.CodeGenerationCTR;
using Ato.EN.IntegrationServices.CodeGenerationRDTIS;
using DataContracts;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using VaTS;

namespace Ato.CD.Inbound.CTR202602
{
    internal class CrossFormValidatorRDTIS : ICrossFormValidator
    {
        private CTR2026 ParentDocument { get; }
        private RDTIS2023 ChildDocument { get; }
        public List<ProcessMessageDocument> Response { get; private set; }

        static int expenditureCap = Int32.Parse("150,000,000", System.Globalization.NumberStyles.Number); //ExpenditureCap = $150 million, Constant Tab in VR Artefacts;
        static decimal refundablePremium = 0.185M; //18.5%, Constant Tab in VR Artefacts;

        internal CrossFormValidatorRDTIS(CTR2026 report, BusinessDocument childDocument)
        {
            ParentDocument = report;
            RDTIS2023XmlConsumer consumer = new RDTIS2023XmlConsumer();
            childDocument.DocumentStream.Position = 0;
            ChildDocument = consumer.Consume(childDocument.DocumentStream, true);
            Response = new List<ProcessMessageDocument>();
        }

        public List<ProcessMessageDocument> ValidateCrossFormRules()
        {
            //Parent Cross From Validation Rules
            VRATOCTR500332();
            VRATOCTR500333();
            VRATOCTRW00028();

            //Child Cross From Validation Rules
            VRATOGEN402009();
            VRATORDTIS438104();
            
            return Response;
        }

        #region VR.ATO.CTR.500332 

        /*  VR.ATO.CTR.500332
        
        Refundable R&D tax offset calculated value is incorrect

        Technical Business Rule Format:
        IF [RDTIS36] = FALSE AND [RDTIS37] = FALSE AND [RDTIS46] > 0 AND [RDTIS46] <> (RefundableOffsetCalc() +/- 1)
            RETURN VALIDATION MESSAGE
        ENDIF
        
        Data Elements:

        ^RDTIS36 = RDTIS:RP:AggregatedTurnover:TaxConcession.ExemptEntityOwnership.Indicator
        ^RDTIS37 = RDTIS:RP:AggregatedTurnover:TaxConcession.AggregatedTurnoverAtOrAboveThreshold.Indicator
        ^RDTIS46 = RDTIS:RP:RefundableTaxOffset:TaxConcession.ResearchAndDevelopment.TaxOffsetRefundable.Amount
        ^Calculated Refundable R&D Tax Offset
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOCTR500332()
        {
            ProcessMessageDocument processMessage;
            decimal refundableOffset = 0;
            bool assertion = false;

            if (ChildDocument.RDTIS36 != null && ChildDocument.RDTIS36 == false &&
                 ChildDocument.RDTIS37 != null && ChildDocument.RDTIS37 == false &&
                 ChildDocument.RDTIS46 != null && ChildDocument.RDTIS46.GetValueOrDefault() > 0)
            {
                refundableOffset = CalcRefundableOffset();
                assertion = OutsideRange(ChildDocument.RDTIS46.GetValueOrDefault(), refundableOffset, 1); 
            }

            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.CTR.500332",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Refundable R&D tax offset calculated value is incorrect",
                    Location = "/tns:RDTIS/tns:RP/tns:RefundableTaxOffset/tns:TaxConcessionResearchAndDevelopmentTaxOffsetRefundableA",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.CTR.500332" } },
                    DocumentSequence = "parent_doc"
                };

                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS36", Value = CTR2026Validator.GetValueOrEmpty(ChildDocument.RDTIS36) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS37", Value = CTR2026Validator.GetValueOrEmpty(ChildDocument.RDTIS37) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS46", Value = CTR2026Validator.GetValueOrEmpty(ChildDocument.RDTIS46) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "Calculated Refundable R&D Tax Offset", Value = CTR2026Validator.GetValueOrEmpty(refundableOffset) });
                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.CTR.500332

        #region VR.ATO.CTR.500333

        /*  VR.ATO.CTR.500333
        Non-refundable R&D tax offset calculated value is incorrect

        Technical Business Rule Format:
        IF [RDTIS36] = TRUE OR [RDTIS37] = TRUE) AND [RDTIS48] > 0 AND [RDTIS48] <> (NonRefundableOffsetCalc() +/- 1)
            RETURN VALIDATION MESSAGE
        ENDIF
            
        Data Elements:

        ^RDTIS36 = RDTIS:RP:AggregatedTurnover:TaxConcession.ExemptEntityOwnership.Indicator
        ^RDTIS37 = RDTIS:RP:AggregatedTurnover:TaxConcession.AggregatedTurnoverAtOrAboveThreshold.Indicator
        ^RDTIS48 = RDTIS:RP:NonRefundableTaxOffset:TaxConcession.ResearchAndDevelopment.TaxOffsetNonRefundable.Amount
        ^Calculated Refundable R&D Tax Offset
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOCTR500333()
        {
            ProcessMessageDocument processMessage;
            decimal NonRefundableOffset = 0;
            bool assertion = false;

            if ((ChildDocument.RDTIS36 != null && ChildDocument.RDTIS36 == true ||
                  ChildDocument.RDTIS37 != null && ChildDocument.RDTIS37 == true) &&
                 (ChildDocument.RDTIS48 != null && ChildDocument.RDTIS48.GetValueOrDefault() > 0))
            {
                NonRefundableOffset = calcNonRefundableOffset();
                assertion = OutsideRange(ChildDocument.RDTIS48.GetValueOrDefault(), NonRefundableOffset, 1);
            }

            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.CTR.500333",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Non-refundable R&D tax offset calculated value is incorrect",
                    Location = "/tns:RDTIS/tns:RP/tns:NonRefundableTaxOffset/tns:TaxConcessionResearchAndDevelopmentTaxOffsetNonRefundableA",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.CTR.500333" } },
                    DocumentSequence = "parent_doc"
                };

                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS36", Value = CTR2026Validator.GetValueOrEmpty(ChildDocument.RDTIS36) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS37", Value = CTR2026Validator.GetValueOrEmpty(ChildDocument.RDTIS37) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS48", Value = CTR2026Validator.GetValueOrEmpty(ChildDocument.RDTIS48) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "Calculated Refundable R&D Tax Offset", Value = CTR2026Validator.GetValueOrEmpty(NonRefundableOffset) });
                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.CTR.500333

        #region VR.ATO.CTR.W00028

        /*  VR.ATO.CTR.W00028
        We use information in this form to meet our obligation to publish R&D expenditure companies claim. This will include publishing your company name, ABN or ACN and claim for notional deductions. For more information, see ato.gov.au

        Legacy Rule Format:
        IF (COUNT(SCHEDULE = "RDTIS") = 1) AND [RDTIS24] <> NULL OR [RDTIS73] <> NULL
            RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:

        ^RDTIS24 = RDTIS:RP:Expense.ResearchAndDevelopment.AllocatedTotal.Amount
        ^RDTIS73 = RDTIS:RP:Expense.ResearchAndDevelopment.AdjustmentBalance.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOCTRW00028()
        {
            ProcessMessageDocument processMessage;
            bool assertion = ChildDocument.RDTIS24 != null || ChildDocument.RDTIS73 != null;

            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.CTR.W00028",
                    Severity = ProcessMessageSeverity.Warning,
                    Description = @"We use information in this form to meet our obligation to publish R&D expenditure companies claim. This will include publishing your company name, ABN or ACN and claim for notional deductions less feedstock adjustments. For more information, see ato.gov.au/RDTI",
                    Location = "/xbrli:xbrl/tns:Report.TargetFinancial.Year",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.CTR.W00028" } },
                    DocumentSequence = "parent_doc"
                };

                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS24", Value = CTR2026Validator.GetValueOrEmpty(ChildDocument.RDTIS24) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS73", Value = CTR2026Validator.GetValueOrEmpty(ChildDocument.RDTIS73) });
                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.CTR.W00028

        #region VR.ATO.GEN.402009

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

        Legacy Rule Format:
        IF (RP:entity.identifier.TFN <> PARENT RETURN:RP:entity.identifier.TFN)
        RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:

        ^CTR TFN = CTR:RP:entity.identifier.TFN
        ^RDTIS70 = RDTIS:RP:Identifiers.TaxFileNumber.Identifier
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOGEN402009()
        {
            ProcessMessageDocument processMessage;
            bool assertion = !ParentDocument.RPIdentifierTFN.Equals(ChildDocument.RDTIS70);

            if (assertion)
            {
                processMessage = new ProcessMessageDocument
                {
                    Code = "CMN.ATO.GEN.402009",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Your supplied TFN does not match the TFN supplied on the form it was submitted with",
                    LongDescription = @"Your supplied TFN does not match the TFN supplied on the parent form it was submitted with",
                    Location = "/tns:RDTIS/tns:RP/tns:TaxFileNumberId",
                    Parameters = new ProcessMessageParameters { new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.GEN.402009" } }
                };

                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "CTR TFN", Value = ParentDocument.RPIdentifierTFN });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS70", Value = ChildDocument.RDTIS70 });
                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.GEN.402009

        #region  VR.ATO.RDTIS.438104

        /* VR.ATO.RDTIS.438104
        Reporting period on Schedule must match reporting period on main form

        Legacy Rule Format:
        IF ([RDTIS71] <> PARENT RETURN PERIOD START DATE) OR ([RDTIS72] <> PARENT RETURN PERIOD END DATE)
            RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:

        ^RDTIS71 = RDTIS:RP:Period.Start.Date
        ^CTR Start Date = CTR:RPStartDate
        ^RDTIS72 = RDTIS:RP:Period.End.Date
        ^CTR End Date = CTR:RPEndDate
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATORDTIS438104()
        {
            ProcessMessageDocument processMessage;
            bool assertion = ((ChildDocument.RDTIS71.HasValue && ChildDocument.RDTIS71 != ParentDocument.RPStartDate)
                            || ChildDocument.RDTIS72.HasValue && ChildDocument.RDTIS72 != ParentDocument.RPEndDate);

            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RDTIS.438104",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Reporting period does not match the reporting period supplied on the form it was submitted with",
                    Location = "/tns:RDTIS/tns:RP/tns:PeriodStartD",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.RDTIS.438104" } },
                };

                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS71", Value = CTR2026Validator.GetValueOrEmptyDate(ChildDocument.RDTIS71) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "CTR Start Date", Value = CTR2026Validator.GetValueOrEmptyDate(ParentDocument.RPStartDate) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "RDTIS72", Value = CTR2026Validator.GetValueOrEmptyDate(ChildDocument.RDTIS72) });
                processMessage.Parameters.Add(new ProcessMessageParameter { Name = "CTR End Date", Value = CTR2026Validator.GetValueOrEmptyDate(ParentDocument.RPEndDate) });
                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.RDTIS.438104
      
        private static bool OutsideRange(decimal field, decimal expression, int range)
        {
            bool response;

            response = (field < (expression - range)) || (field > (expression + range));

            return response;
        }

        #region CalcRefundableOffset

        /*
        IF ([RDTIS45] < ExpenditureCap) THEN
             [RDTIS45] * ([RDTIS78] + RefundablePremium)
        ELSE 
             (ExpenditureCap * ([RDTIS78] + RefundablePremium)) + (([RDTIS45] - ExpenditureCap) * [RDTIS78])
        ENDIF
        */
        private decimal CalcRefundableOffset()
        {
            if (ChildDocument.RDTIS45 < expenditureCap)
            {
                return (decimal)(ChildDocument.RDTIS45.GetValueOrDefault() * ((ChildDocument.RDTIS78.GetValueOrDefault() / 100) + refundablePremium));
            }
            else
            {
                return (decimal)((expenditureCap * ((ChildDocument.RDTIS78.GetValueOrDefault() / 100) + refundablePremium)) + ((ChildDocument.RDTIS45.GetValueOrDefault() - expenditureCap) * (ChildDocument.RDTIS78.GetValueOrDefault() / 100)));
            }
        }

        #endregion

        #region calcNonRefundableOffset

        /*
        IF ([RDTIS47] > ExpenditureCap AND [RDTIS81] = 0)  THEN
        Intensity = 100
        ExcessOffset = ([RDTIS47] - ExpenditureCap) * [RDTIS78]
     
        ELSE IF [RDTIS81] = 0 THEN
        Intensity = 100
        ExcessOffset = 0 
     
        ELSE IF ([RDTIS47] > ExpenditureCap) THEN
        Intensity = (ExpenditureCap / [RDTIS81])
        ExcessOffset = ([RDTIS47] - ExpenditureCap) * [RDTIS78]
     
        ELSE
        Intensity = ([RDTIS47] / [RDTIS81])
        ExcessOffset = 0
     
        ENDIF
           
        IF (Intensity > 0 AND Intensity <= T1Intensity) THEN
            IF ([RDTIS47] > ExpenditureCap) THEN
                T1Deductions = ExpenditureCap
            ELSE
                T1Deductions = [RDTIS47]
            ENDIF
        ELSE
            T1Deductions = T1Intensity * [RDTIS81]
            IF [RDTIS47] > ExpenditureCap THEN
                T2Deductions = (ExpenditureCap - T1Deductions)
            ELSE
                T2Deductions = ([RDTIS47] - T1Deductions)
            ENDIF
        ENDIF

        T1Offset = (T1Deductions * ([RDTIS78] + T1Premium))
        T2Offset = (T2Deductions * ([RDTIS78] + T2Premium))

        NonRefundableOffset = T1Offset + T2Offset + ExcessOffset
         */
        private decimal calcNonRefundableOffset()
        {
            decimal intensity = 0, excessOffset = 0;

            // T1,T2  Deduction Calculation Variable
            decimal t1Intensity = 0.02M; // 2%;

            //decimal t2Intensity = 0.02+M; // 2+% ;  
            decimal t1Deductions = 0, t2Deductions = 0;

            // Premium %
            decimal t1Premium = 0.085M; // 8.5%;
            decimal t2Premium = 0.165M; // 16.5%;

            // intensity and excessOffset Calculation 
            if (ChildDocument.RDTIS47.GetValueOrDefault() > expenditureCap && ChildDocument.RDTIS81.GetValueOrDefault() == 0)
            {
                intensity = 100;
                excessOffset = (ChildDocument.RDTIS47.GetValueOrDefault() - expenditureCap) * (ChildDocument.RDTIS78.GetValueOrDefault() / 100);
            }
            else if (ChildDocument.RDTIS81.GetValueOrDefault() == 0)
            {
                intensity = 100;
                excessOffset = 0;

            }
            else if (ChildDocument.RDTIS47.GetValueOrDefault() > expenditureCap)
            {
                intensity = (expenditureCap / ChildDocument.RDTIS81.GetValueOrDefault());
                excessOffset = (ChildDocument.RDTIS47.GetValueOrDefault() - expenditureCap) * (ChildDocument.RDTIS78.GetValueOrDefault() / 100);
            }
            else
            {
                intensity = (ChildDocument.RDTIS47.GetValueOrDefault() / ChildDocument.RDTIS81.GetValueOrDefault());
                excessOffset = 0;
            }

            // T1,T2 Deduction Calculation
            if (intensity > 0 && intensity <= t1Intensity) // Tier1 deduction
            {
                t1Deductions = ChildDocument.RDTIS47.GetValueOrDefault() > expenditureCap ? expenditureCap : ChildDocument.RDTIS47.GetValueOrDefault();
            }
            else
            {
                t1Deductions = t1Intensity * ChildDocument.RDTIS81.GetValueOrDefault();              
                t2Deductions = ChildDocument.RDTIS47.GetValueOrDefault() > expenditureCap ? (expenditureCap - t1Deductions) : (ChildDocument.RDTIS47.GetValueOrDefault() - t1Deductions);
            }
          
            return Math.Round(((t1Deductions * ((ChildDocument.RDTIS78.GetValueOrDefault() / 100) + t1Premium)) + (t2Deductions * ((ChildDocument.RDTIS78.GetValueOrDefault() / 100) + t2Premium)) + excessOffset), 2, MidpointRounding.AwayFromZero);
        }

        #endregion
    }
}