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

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

        internal CrossFormValidatorLS(FITR2025 report, BusinessDocument childDocument)
        {
            ParentDocument = report;
            ChildDocument = (LS2017)childDocument.ConsumedReport;
            Contexts = ChildDocument.GetContexts();
            Response = new List<ProcessMessageDocument>();
        }

        public List<ProcessMessageDocument> ValidateCrossFormRules()
        {
            //Parent form cross form rules
            VRATOFITR434086();
            VRATOFITR434087();

            //Child form cross form rules
            VRATOGEN402009();
            VRATOGEN402010();
            VRATOGEN428045();
            VRATOGEN438000();
            VRATOGEN438001();
            VRATOLS414017();
            VRATOLS414022();
            
            return Response;
        }

        #region VR.ATO.FITR.434086

        /*  VR.ATO.FITR.434086
        Tax losses carried forward to later income years in schedule must be equal to the amount in main form

        Legacy Rule Format:
        (CountDocument('LS') = 1) AND ^LS50 <> ^FITR76

        Data Elements:
        ^FITR76 = FITR:RP:Tax.Losses.CarriedForward.LaterIncomeYearsTotal.Amount
        ^LS50 = LS:RP.Closing: Tax.Losses.CarriedForward.Total.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOFITR434086()
        {
            bool assertion = ParentDocument.FITR76.GetValueOrDefault() != ChildDocument.LS50.GetValueOrDefault();

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.GEN.434086",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Tax losses carried forward to later income years in schedule must equal amount in main form",
                    LongDescription = @"The total of the 'Tax losses carried forward to later income years'  for unconsolidated entities in the Losses schedule must equal 'Tax losses carried forward to later income years' on the main form",
                    Location = "/tns:FITR/tns:RP/tns:Losses/tns:TaxCarriedForwardLaterIncomeYearsTotalA",
                    Parameters = new ProcessMessageParameters() {
                        new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.FITR.434086" },
                        new ProcessMessageParameter() { Name = "FITR76", Value = FITR2025Validator.GetValueOrEmpty(ParentDocument.FITR76) },
                        new ProcessMessageParameter() { Name = "LS50", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS50) }
                    },
                    DocumentSequence = "parent_doc"
                };

                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.FITR.434086

        #region VR.ATO.FITR.434087

        /*  VR.ATO.FITR.434087
        Net capital losses carried forward to later income years must equal amount in Losses schedule

        Legacy Rule Format:
        (CountDocument('LS') = 1) AND ^LS52 <> ^FITR207

        Data Elements:
        ^FITR207 = FITR:RP:Capital.Losses.CarriedForward.Net.Amount
        ^LS52 = LS:RP.Closing(Instant):Capital.Losses.CarriedForward.Net.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOFITR434087()
        {
            bool assertion = ParentDocument.FITR207.GetValueOrDefault() != ChildDocument.LS52.GetValueOrDefault();

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.GEN.434087",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Net capital losses carried forward to later income years must equal amount in Losses schedule",
                    LongDescription = @"The total of the 'Net capital losses carried forward to later income years'  for unconsolidated entities in the Losses schedule must equal 'Net capital losses carried forward to later income years' on the main form",
                    Location = "/tns:FITR/tns:RP/tns:Losses/tns:CapitalCarriedForwardNetA",
                    Parameters = new ProcessMessageParameters() {
                        new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.FITR.434087" },
                        new ProcessMessageParameter() { Name = "FITR207", Value = FITR2025Validator.GetValueOrEmpty(ParentDocument.FITR207) },
                        new ProcessMessageParameter() { Name = "LS52", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS52) }
                    },
                    DocumentSequence = "parent_doc"
                };

                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.FITR.434087

        #region VR.ATO.GEN.402009

        /*  VR.ATO.GEN.402009
        Net capital losses carried forward to later income years must equal amount in Losses schedule

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

        Data Elements:
        ^FITR5 = FITR:RP:Identifiers.TaxFileNumber.Identifier
        ^LS TFN = LS TFN
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOGEN402009()
        {
            bool assertion = !ParentDocument.FITR5.Equals(ChildDocument.RPIdentifierTFN);

            if (assertion)
            {
                ProcessMessageDocument 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 = "/xbrli:xbrl/xbrli:context/xbrli:entity/xbrli:identifier",
                    Parameters = new ProcessMessageParameters {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.GEN.402009" },
                        new ProcessMessageParameter { Name = "FITR5", Value = ParentDocument.FITR5 },
                        new ProcessMessageParameter { Name = "LS TFN", Value = ChildDocument.RPIdentifierTFN }
                    }
                };

                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.GEN.402009

        #region VR.ATO.GEN.402010

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

        Legacy Rule Format:
        IF (RP:pyid.xx.xx:Identifiers.AustralianBusinessNumber.Identifier <> NULLORBLANK) AND (PARENT RETURN:RP:pyid.xx.xx:Identifiers.AustralianBusinessNumber.Identifier <> NULLORBLANK) AND (RP:pyid.xx.xx:Identifiers.AustralianBusinessNumber.Identifier <> PARENT RETURN:RP:pyid.xx.xx:Identifiers.AustralianBusinessNumber.Identifier)
            RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:
        ^FITR7 = FITR:RP:Identifiers.AustralianBusinessNumber.Identifier
        ^LS3 = LS:RP:Identifiers.AustralianBusinessNumber.Identifier
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOGEN402010()
        {
            bool assertion = !string.IsNullOrWhiteSpace(ParentDocument.FITR7) && !string.IsNullOrWhiteSpace(ChildDocument.LS3) && !ParentDocument.FITR7.Equals(ChildDocument.LS3);

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.GEN.402010",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Your supplied ABN does not match the ABN supplied on the form it was submitted with",
                    LongDescription = @"Your supplied ABN does not match the ABN supplied on the parent form it was submitted with",
                    Location = "/xbrli:xbrl/tns:Identifiers.AustralianBusinessNumber.Identifier",
                    Parameters = new ProcessMessageParameters() {
                        new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.GEN.402010" },
                        new ProcessMessageParameter() { Name = "FITR7", Value = ParentDocument.FITR7 },
                        new ProcessMessageParameter() { Name = "LS3", Value = ChildDocument.LS3 }
                    }
                };

                Response.Add(processMessage);
            }
        }

        #endregion //VR.ATO.GEN.402010

        #region VR.ATO.GEN.428045

        /*  VR.ATO.GEN.428045
        Organisation Name in schedule should match Organisation Name in parent return

        Legacy Rule Format:
        IF RP:pyde.xx.xx:OrganisationNameDetails.OrganisationalName.Text IN TUPLE (xbrli\organisationname1.xx.xx:OrganisationNameDetails) <> NULL
        AND RP:pyde.xx.xx:OrganisationNameDetails.OrganisationalName.Text IN TUPLE (xbrli\organisationname1.xx.xx:OrganisationNameDetails) <> (PARENT RETURN:RP:pyde.xx.xx:OrganisationNameDetails.OrganisationalName.Text IN TUPLE (xbrli\organisationname2.xx.xx:OrganisationNameDetails)
        WHERE (TUPLE EXPLICIT pyde.xx.xx:OrganisationNameDetails.OrganisationalNameType.Code = "MN")
        AND (TUPLE EXPLICIT pyde.xx.xx:OrganisationNameDetails.Currency.Code = "C")) 
            RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:
        ^FITR6 = FITR:RP:OrganisationNameDetails.OrganisationalName.Text
        ^LS2 = LS:RP:pyin.02.00:OrganisationNameDetails.OrganisationalName.Text
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOGEN428045()
        {
            bool assertion = !string.IsNullOrWhiteSpace(ParentDocument.FITR6) && !string.IsNullOrWhiteSpace(ChildDocument.LS2) && !ParentDocument.FITR6.Equals(ChildDocument.LS2);

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.GEN.428045",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Organisation Name in schedule should match Organisation Name in parent return",
                    Location = "/xbrli:xbrl/tns:OrganisationNameDetails.OrganisationalName.Text",
                    Parameters = new ProcessMessageParameters() {
                        new ProcessMessageParameter() { Name = "RuleIdentifier", Value = "VR.ATO.GEN.428045" },
                        new ProcessMessageParameter() { Name = "FITR6", Value = ParentDocument.FITR6 },
                        new ProcessMessageParameter() { Name = "LS2", Value = ChildDocument.LS2 }
                    }
                };

                Response.Add(processMessage);
            }
        }

        #endregion //VR.ATO.GEN.428045

        #region VR.ATO.GEN.438000

        /*  VR.ATO.GEN.438000
        Period start date within context on the schedule does not match period start date within PARENT RETURN:reporting party context

        Legacy Rule Format:
        IF (period.startDate WHERE CONTEXT(ALL)) <> PARENT RETURN:RP:period.startDate
           RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:
        ^FITR300 = FITR:RP:Period.Start.Date
        ^LS Start Date = LS Start Date
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOGEN438000()
        {
            IEnumerable<Context> contexts = Contexts.Where(context => context.StartDate.GetValueOrDefault() != ParentDocument.FITR300.GetValueOrDefault());

            IEnumerable<ProcessMessageDocument> processMessages = contexts.Select(context => new ProcessMessageDocument
            {
                Code = "CMN.ATO.GEN.438000",
                Severity = ProcessMessageSeverity.Error,
                Description = @"The context period start date is incorrect.",
                LongDescription = @"Period start date within context on the schedule must match period start date within the parent return reporting party context",
                Location = $"/xbrli:xbrl/xbrli:context[{context.Index}]/xbrli:period/xbrli:startDate",
                Parameters = new ProcessMessageParameters {
                    new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.GEN.438000" },
                    new ProcessMessageParameter { Name = "FITR300", Value = FITR2025Validator.GetValueOrEmptyDate(ParentDocument.FITR300) },
                    new ProcessMessageParameter { Name = "LS Start Date", Value = FITR2025Validator.GetValueOrEmptyDate(context.StartDate) }
                }
            });

            Response.AddRange(processMessages);
        }

        #endregion // VR.ATO.CGTS.438000

        #region VR.ATO.GEN.438001

        /*  VR.ATO.GEN.438001
        Period end date within context on the schedule does not match period end date within PARENT RETURN:reporting party context

        Legacy Rule Format:
        IF (period.endDate WHERE CONTEXT(ALL)) <> PARENT RETURN:RP:period.endDate
            RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:
        ^FITR301 = FITR:RP:Period.End.Date
        ^LS End Date = LS End Date
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOGEN438001()
        {
            IEnumerable<Context> contexts = Contexts.Where(context => context.EndDate.GetValueOrDefault() != ParentDocument.FITR301.GetValueOrDefault());

            IEnumerable<ProcessMessageDocument> processMessages = contexts.Select(context => new ProcessMessageDocument
            {
                Code = "CMN.ATO.GEN.438001",
                Severity = ProcessMessageSeverity.Error,
                Description = @"The context period end date is incorrect.",
                LongDescription = @"Period end date within context on the schedule must match period end date within the parent return reporting party context",
                Location = $"/xbrli:xbrl/xbrli:context[{context.Index}]/xbrli:period/xbrli:endDate",
                Parameters = new ProcessMessageParameters {
                    new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.GEN.438001" },
                    new ProcessMessageParameter { Name = "FITR301", Value = FITR2025Validator.GetValueOrEmptyDate(ParentDocument.FITR301) },
                    new ProcessMessageParameter { Name = "LS End Date", Value = FITR2025Validator.GetValueOrEmptyDate(context.EndDate) }
                }
            });

            Response.AddRange(processMessages);
        }

        #endregion // VR.ATO.CGTS.438001

        #region VR.ATO.LS.414017

        /*  VR.ATO.LS.414017
        Only companies and widely held trusts are required to indicate whether continuity of majority ownership test passed for each year of loss.

        Technical Business Rule:
        IF (PARENT RETURN <> SET("CTR", "TRT")) AND (([LS15] <>NULL) OR ([LS16] <> NULL) OR ([LS17] <> NULL) OR ([LS18] <> NULL) OR ([LS19] <> NULL) OR ([LS53] <> NULL))
        RETURN VALIDATION MESSAGE
        ENDIF

        Data Elements:
        ^LS15 = LS:RP.Y0:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
        ^LS16 = LS:RP.Y0-1:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
        ^LS17 = LS:RP.Y0-2:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
        ^LS18 = LS:RP.Y0-3:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
        ^LS19 = LS:RP.Y0-4:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
        ^LS53 = LS:RP.Y0-5+:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOLS414017()
        {
            bool assertion = ChildDocument.LS15.HasValue || ChildDocument.LS16.HasValue || ChildDocument.LS17.HasValue || ChildDocument.LS18.HasValue || ChildDocument.LS19.HasValue || ChildDocument.LS53.HasValue;

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument
                {
                    Code = "CMN.ATO.LS.414017",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Continuity of majority ownership test only required for companies or widely held trusts",
                    Location = "/xbrli:xbrl/ls:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator",
                    Parameters = new ProcessMessageParameters {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.LS.414017" },
                        new ProcessMessageParameter { Name = "LS15", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS15) },
                        new ProcessMessageParameter { Name = "LS16", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS16) },
                        new ProcessMessageParameter { Name = "LS17", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS17) },
                        new ProcessMessageParameter { Name = "LS18", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS18) },
                        new ProcessMessageParameter { Name = "LS19", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS19) },
                        new ProcessMessageParameter { Name = "LS53", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS53) }
                    }
                };

                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.LS.414017

        #region VR.ATO.LS.414022

        /*  VR.ATO.LS.414022
        Questions relating to unrealised losses are to be completed by companies only. Funds and trusts are not required to complete.

        Technical Business Rule:
        IF (PARENT RETURN <> "CTR") AND ([LS25] <> NULL OR [LS26] <> NULL OR [LS27] <> NULL OR [LS28] <> NULL)
            RETURN VALIDATION MESSAGE
        ENDIF
        
        Data Elements:
        ^LS25 = LS:RP:rvctc3.xx.xx:Tax.Losses.ChangeOfOwnershipOrControlAfterReferenceTime.Indicator
        ^LS26 = LS:RP:rvctc3.xx.xx:Tax.Losses.MaximumNetAssetValueTestPassedAtChangeoverTime.Indicator
        ^LS27 = LS:RP:rvctc3.xx.xx:Tax.Losses.UnrealisedNetLossAtChangeoverTime.Indicator
        ^LS28 = LS:RP:rvctc3.xx.xx:Tax.Losses.UnrealisedNetLossAtChangeoverTime.Amount
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public virtual void VRATOLS414022()
        {
            bool assertion = ChildDocument.LS25.HasValue || ChildDocument.LS26.HasValue || ChildDocument.LS27.HasValue || ChildDocument.LS28.HasValue;

            if (assertion)
            {
                ProcessMessageDocument processMessage = new ProcessMessageDocument
                {
                    Code = "CMN.ATO.LS.414022",
                    Severity = ProcessMessageSeverity.Error,
                    Description = @"Unrealised losses are not required",
                    Location = "/xbrli:xbrl/ls:Tax.Losses.ChangeOfOwnershipOrControlAfterReferenceTime.Indicator",
                    Parameters = new ProcessMessageParameters {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.LS.414022" },
                        new ProcessMessageParameter { Name = "LS25", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS25) },
                        new ProcessMessageParameter { Name = "LS26", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS26) },
                        new ProcessMessageParameter { Name = "LS27", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS27) },
                        new ProcessMessageParameter { Name = "LS28", Value = FITR2025Validator.GetValueOrEmpty(ChildDocument.LS28) }
                    }
                };

                Response.Add(processMessage);
            }
        }

        #endregion // VR.ATO.LS.414022
    }
}