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

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

        private LS2017 ChildDocument { get; }

        private List<Context> Contexts { get; }

        internal CrossFormValidatorLS(SMSFAR2026 report, BusinessDocument childDocument)
        {
            ParentDocument = report;
            ChildDocument = (LS2017)childDocument.ConsumedReport;
            Contexts = GetContexts();
        }

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

            VRATOGEN402009(response);
            VRATOGEN402010(response);
            VRATOGEN438000(response);
            VRATOGEN438001(response);
            VRATOGEN428045(response);
            VRATOLS414017(response);
            VRATOLS414022(response);
            VRATOSMSFAR436129(response);
            VRATOSMSFAR436801(response);

            return response;
        }

        #region VR.ATO.GEN.402009
        /* VR.ATO.GEN.402009
            IF RP:entity.identifier.TFN<> PARENT RETURN:RP:entity.identifier.TFN
                RETURN VALIDATION MESSAGE
            ENDIF
                xbrli:context[@id =$RP.LOS.ContextId]
            RP.LOS.Context
        */
        private void VRATOGEN402009(List<ProcessMessageDocument> response)
        {
            bool assertion = !ParentDocument.RPIdentifierTFN.Equals(ChildDocument.RPIdentifierTFN);

            if (assertion)
            {
                response.Add(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" } }
                });
            }
        }
        #endregion

        #region VR.ATO.GEN.402010
        /* VR.ATO.GEN.402009
            IF (RP:tns:Identifiers.AustralianBusinessNumber.Identifier <> NULLORBLANK)
                AND (PARENT RETURN:RP:tns:Identifiers.AustralianBusinessNumber.Identifier <> NULLORBLANK)
                AND (RP:tns:Identifiers.AustralianBusinessNumber.Identifier <> PARENT RETURN:RP:tns:Identifiers.AustralianBusinessNumber.Identifier)
                RETURN VALIDATION MESSAGE
            ENDIF
        */
        private void VRATOGEN402010(List<ProcessMessageDocument> response)
        {
            bool assertion = !string.IsNullOrWhiteSpace(ParentDocument.SMSFAR5) &&
                             !string.IsNullOrWhiteSpace(ChildDocument.LS3) &&
                             !ParentDocument.SMSFAR5.Equals(ChildDocument.LS3);
            if (assertion)
            {
                response.Add(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" } }
                });
            }
        }
        #endregion

        #region VR.ATO.GEN.438000
        /*  VR.ATO.GEN.438000
            IF (period.startDate WHERE CONTEXT(ALL)) <> PARENT RETURN:RP:period.startDate
               RETURN VALIDATION MESSAGE
            ENDIF
        */
        private void VRATOGEN438000(List<ProcessMessageDocument> response)
        {
            response.AddRange(from context in Contexts
                              where context.StartDate.GetValueOrDefault() != ParentDocument.RPStartDate.GetValueOrDefault()
                select 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" } }
                });
        }
        #endregion

        #region VR.ATO.GEN.438001
        /*  VR.ATO.GEN.438001
            IF (period.endDate WHERE CONTEXT(ALL)) <> PARENT RETURN:RP:period.endDate
               RETURN VALIDATION MESSAGE
            ENDIF
        */
        private void VRATOGEN438001(List<ProcessMessageDocument> response)
        {
            response.AddRange(from context in Contexts
                              where context.EndDate.GetValueOrDefault() != ParentDocument.RPEndDate.GetValueOrDefault()
                select 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" } }
                });
        }
        #endregion

        #region VR.ATO.GEN.428045
        /* VR.ATO.GEN.428045
            IF RP:pyde.xx.xx:OrganisationNameDetails.OrganisationalName.Text 
                IN TUPLE (xbrli\orgname1.xx.xx:OrganisationNameDetails) <> NULL 
                AND RP:pyde.xx.xx:OrganisationNameDetails.OrganisationalName.Text IN TUPLE 
                (xbrli\orgname1.xx.xx:OrganisationNameDetails) <> 
                (PARENT RETURN:RP:pyde.xx.xx:OrganisationNameDetails.OrganisationalName.Text 
                IN TUPLE (xbrli\orgname2.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
        */
        private void VRATOGEN428045(List<ProcessMessageDocument> response)
        {
            bool assertion = !string.IsNullOrWhiteSpace(ParentDocument.SMSFAR4) &&
                             !string.IsNullOrWhiteSpace(ChildDocument.LS2) &&
                             !ParentDocument.SMSFAR4.ToUpper().Equals(ChildDocument.LS2.ToUpper()) &&
                             ParentDocument.SMSFAR234.Equals("MN") && ParentDocument.SMSFAR319.Equals("C");
            if (assertion)
            {
                response.Add(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/tns:OrganisationNameDetails.OrganisationalName.Text",
                    Parameters = new ProcessMessageParameters { new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.GEN.428045" } }
                });
            }
        }
        #endregion

        #region VR.ATO.LS.414017
        /* VR.ATO.LS.414022
            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
            [LS15] = LS:RP.Y0:tns:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
            [LS16] = LS:RP.Y0-1:tns:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
            [LS17] = LS:RP.Y0-2:tns:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
            [LS18] = LS:RP.Y0-3:tns:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
            [LS19] = LS:RP.Y0-4:tns:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
            [LS53] = LS:RP.Y0-5+:tns:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator
        */
        private void VRATOLS414017(List<ProcessMessageDocument> response)
        {
            bool assertion = ChildDocument.LS15.HasValue || ChildDocument.LS16.HasValue ||
                             ChildDocument.LS17.HasValue || ChildDocument.LS18.HasValue ||
                             ChildDocument.LS19.HasValue || ChildDocument.LS53.HasValue;
            if (assertion)
            {
                response.Add(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/tns:Tax.Losses.CarriedForward.ContinuityOfMajorityOwnershipTestPassed.Indicator",
                    Parameters = new ProcessMessageParameters { new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.LS.414017" } }
                });
            }
        }
        #endregion

        #region VR.ATO.LS.414022
        /* VR.ATO.LS.414022
            IF (PARENT RETURN<>"CTR") AND ([LS25] <> NULL OR [LS26] <> NULL OR [LS27] <> NULL OR [LS28] <> NULL)
                RETURN VALIDATION MESSAGE
            ENDIF
            
            [LS25] = LS:RP:tns:Tax.Losses.ChangeOfOwnershipOrControlAfterReferenceTime.Indicator
            [LS26] = LS:RP:tns:Tax.Losses.MaximumNetAssetValueTestPassedAtChangeoverTime.Indicator
            [LS27] = LS:RP:tns:Tax.Losses.UnrealisedNetLossAtChangeoverTime.Indicator
            [LS28] = LS:RP:tns:Tax.Losses.UnrealisedNetLossAtChangeoverTime.Amount
        */
        private void VRATOLS414022(List<ProcessMessageDocument> response)
        {
            bool assertion = ChildDocument.LS25.HasValue || ChildDocument.LS26.HasValue ||
                             ChildDocument.LS27.HasValue || ChildDocument.LS28.HasValue;
            if (assertion)
            {
                response.Add(new ProcessMessageDocument
                {
                    Code = "CMN.ATO.LS.414022",
                    Severity = ProcessMessageSeverity.Error,
                    Description = "Unrealised losses are not required",
                    Location = "/xbrli:xbrl/tns:Tax.Losses.ChangeOfOwnershipOrControlAfterReferenceTime.Indicator",
                    Parameters = new ProcessMessageParameters { new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.LS.414022" } },
                });
            }
        }
        #endregion

        #region VR.ATO.SMSFAR.436129
        /*  VR.ATO.SMSFAR.436129
            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

            Legacy Rule Format:
            IF (COUNT(SCHEDULE = "LS") = 1) AND [LS50] <> [SMSFAR85]  
                RETURN VALIDATION MESSAGE
            ENDIF
                    
            [LS50] = LS:RP.ALL:tnsTax.Losses.CarriedForward.Total.Amount
            [SMSFAR85] = SMSFAR2019:RP.Closing:tns:Tax.Losses.CarriedForward.LaterIncomeYearsTotal.Amount

            Technical Business Rule Format:
            (CountDocument('LS') = 1) AND ^LS50 <> ^SMSFAR85

            Data Elements:
            
            RP.Closing:^SMSFAR85 = tns:Tax.Losses.CarriedForward.LaterIncomeYearsTotal.Amount
            
            RP.Closing:^LS50 = LS:RP.ALL:tnsTax.Losses.CarriedForward.Total.Amount
        */
        private void VRATOSMSFAR436129(List<ProcessMessageDocument> response)
        {
            bool assertion = ParentDocument.SMSFAR85.HasValue && ChildDocument.LS50.HasValue &&
                             ParentDocument.SMSFAR85.Value != ChildDocument.LS50.Value;
            if (assertion)
            {
                response.Add(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 = "/xbrli:xbrl/tns:Tax.Losses.CarriedForward.LaterIncomeYearsTotal.Amount",
                    Parameters = new ProcessMessageParameters
                    {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.SMSFAR.436129" },
                        new ProcessMessageParameter { Name = "SMSFAR85", Value = ParentDocument.SMSFAR85.Value.ToString() },
                        new ProcessMessageParameter { Name = "LS50",  Value = ChildDocument.LS50.Value.ToString() }
                    },
                    DocumentSequence = "parent_doc"
                });
            }
        }
        #endregion

        #region VR.ATO.SMSFAR.436801
        /*  VR.ATO.SMSFAR.436801
            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

            Legacy Rule Format:
            IF (COUNT(SCHEDULE = "LS") = 1) AND [LS52] <> [SMSFAR380]  
                RETURN VALIDATION MESSAGE
            ENDIF
                    
            [LS52] = LS:RP.ALL(Instant):tns:Capital.Losses.CarriedForward.Net.Amount
            [SMSFAR380] = SMSFAR2019:RP.Y0Plus.Closing(Instant):tns:Capital.Losses.CarriedForward.Net.Amount

            Technical Business Rule Format:
            (CountDocument('LS') = 1) AND ^LS52 <> ^SMSFAR380

            Data Elements:
            
            RP.Y0Plus.Closing(Instant):^SMSFAR380 = tns:Capital.Losses.CarriedForward.Net.Amount
            
            RP.Y0Plus.Closing(Instant):^LS52 = LS:RP.ALL(Instant):tns:Capital.Losses.CarriedForward.Net.Amount
        */
        private void VRATOSMSFAR436801(List<ProcessMessageDocument> response)
        {
            bool assertion = ParentDocument.SMSFAR380.HasValue && ChildDocument.LS52.HasValue &&
                             ParentDocument.SMSFAR380.Value != ChildDocument.LS52.Value;
            if (assertion)
            {
                response.Add(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 = "/xbrli:xbrl/tns:Capital.Losses.CarriedForward.Net.Amount",
                    Parameters = new ProcessMessageParameters
                    {
                        new ProcessMessageParameter { Name = "RuleIdentifier", Value = "VR.ATO.SMSFAR.436801" },
                        new ProcessMessageParameter { Name = "SMSFAR380", Value = ParentDocument.SMSFAR380.Value.ToString()},
                        new ProcessMessageParameter { Name = "LS52", Value = ChildDocument.LS52.Value.ToString() }
                    },
                    DocumentSequence = "parent_doc"
                });
            }
        }
        #endregion

        private List<Context> GetContexts() => new List<Context>()
            .AddContext(ChildDocument.RPStartDate, ChildDocument.RPEndDate, ChildDocument.RPOccurrenceIndex, ChildDocument.RPId)
            .AddContext(ChildDocument.RPY0StartDate, ChildDocument.RPY0EndDate, ChildDocument.RPY0OccurrenceIndex, ChildDocument.RPY0Id)
            .AddContext(ChildDocument.RPY0Minus1StartDate, ChildDocument.RPY0Minus1EndDate, ChildDocument.RPY0Minus1OccurrenceIndex, ChildDocument.RPY0Minus1Id)
            .AddContext(ChildDocument.RPY0Minus2StartDate, ChildDocument.RPY0Minus2EndDate, ChildDocument.RPY0Minus2OccurrenceIndex, ChildDocument.RPY0Minus2Id)
            .AddContext(ChildDocument.RPY0Minus3StartDate, ChildDocument.RPY0Minus3EndDate, ChildDocument.RPY0Minus3OccurrenceIndex, ChildDocument.RPY0Minus3Id)
            .AddContext(ChildDocument.RPY0Minus4StartDate, ChildDocument.RPY0Minus4EndDate, ChildDocument.RPY0Minus4OccurrenceIndex, ChildDocument.RPY0Minus4Id)
            .AddContext(ChildDocument.RPY0Minus5PStartDate, ChildDocument.RPY0Minus5PEndDate, ChildDocument.RPY0Minus5POccurrenceIndex, ChildDocument.RPY0Minus5PId)
            .AddContext(ChildDocument.RPALLStartDate, ChildDocument.RPALLEndDate, ChildDocument.RPALLOccurrenceIndex, ChildDocument.RPALLId);
    }
}