/***********************************************************************************************************
 * Name         : SynchClearanceTriggerHelper
 * Purpose      : Implementation for methods called by SynchClearanceTriggerHandler
 **********************************************************************************************************/
public with sharing class SynchClearanceTriggerHelper {
    /**
     * @description updates the SynchId__c field based on the SynchIdDigit__c value
     * @param newList new records
     * @param oldMap old records
     */
    public static void updateSynchId(List<SynchClearance__c> newList, Map<Id, SynchClearance__c> oldMap) {
        for (SynchClearance__c synch : newList) {
            if (oldMap == null || synch.SynchIdDigit__c != oldMap.get(synch.Id).SynchIdDigit__c) {
                synch.SynchId__c = SynchUtils.formatSynchId(Integer.valueOf(synch.SynchIdDigit__c));
            }
        }
    }

    /**
     * @description updates the USD Fee field based on the currency conversion rate
     * @param newList new records
     * @param oldMap old records
     */
    public static void updateUsdFee(List<SynchClearance__c> newList, Map<Id, SynchClearance__c> oldMap) {
        List<SynchClearance__c> filteredSynchList = new List<SynchClearance__c>();
        List<String> currenciesList = new List<String>();

        for (SynchClearance__c synch : newList) {
            if (synch.Fee__c != null && synch.CurrencyIsoCode != null) {
                if (
                    oldMap == null ||
                    (oldMap.get(synch.Id).Fee__c != synch.Fee__c ||
                    oldMap.get(synch.Id).CurrencyIsoCode != synch.CurrencyIsoCode)
                ) {
                    filteredSynchList.add(synch);
                    currenciesList.add(synch.CurrencyIsoCode);
                }
            }
        }

        if (!filteredSynchList.isEmpty()) {
            Map<String, List<DatedConversionRate>> mapCurrencyDatedConversionRate = new Map<String, List<DatedConversionRate>>();

            List<DatedConversionRate> conversionRateList = [
                SELECT Id, IsoCode, StartDate, NextStartDate, ConversionRate
                FROM DatedConversionRate
                WHERE IsoCode IN :currenciesList
                ORDER BY IsoCode, StartDate DESC
            ];

            for (DatedConversionRate convRate : conversionRateList) {
                SchemaUtils.addToFieldNameSObjectListMap(mapCurrencyDatedConversionRate, convRate, 'IsoCode');
            }

            for (SynchClearance__c synch : filteredSynchList) {
                if (oldMap == null) {
                    synch.USDFee__c =
                        (1 / mapCurrencyDatedConversionRate.get(synch.CurrencyIsoCode)[0].ConversionRate) *
                        synch.Fee__c;
                } else {
                    for (DatedConversionRate convRate : mapCurrencyDatedConversionRate.get(synch.CurrencyIsoCode)) {
                        if (
                            synch.CreatedDate >= convRate.StartDate &&
                            synch.CreatedDate <= convRate.NextStartDate.addDays(-1)
                        ) {
                            synch.USDFee__c = (1 / convRate.ConversionRate) * synch.Fee__c;
                            break;
                        }
                    }
                }
            }
        }
    }

    /**
     * @description Evalutes DateOfConfirmation__c/PaymentReceivedDate__c and link the
     *  SynchClearance__c record to the respective SynchTarget__c record
     * @param newList new records
     * @param oldMap old records
     */
    public static void linkToSynchTarget(List<SynchClearance__c> newList, Map<Id, SynchClearance__c> oldMap) {
        Map<String, List<SynchClearance__c>> categoryToSynchClearances = new Map<String, List<SynchClearance__c>>();

        for (SynchClearance__c sc : newList) {
            if (
                (oldMap == null && sc.DateOfConfirmation__c != null) ||
                oldMap != null &&
                oldMap.get(sc.Id).DateOfConfirmation__c != sc.DateOfConfirmation__c
            ) {
                if (!categoryToSynchClearances.containsKey(SYNCH_Constants.CONFIRMED_SYNCHS_CATEGORY)) {
                    categoryToSynchClearances.put(
                        SYNCH_Constants.CONFIRMED_SYNCHS_CATEGORY,
                        new List<SynchClearance__c>()
                    );
                }
                categoryToSynchClearances.get(SYNCH_Constants.CONFIRMED_SYNCHS_CATEGORY).add(sc);
            }

            if (
                (oldMap == null && sc.PaymentReceivedDate__c != null) ||
                oldMap != null &&
                oldMap.get(sc.Id).PaymentReceivedDate__c != sc.PaymentReceivedDate__c
            ) {
                if (!categoryToSynchClearances.containsKey(SYNCH_Constants.PAYMENTS_RECEIVED_CATEGORY)) {
                    categoryToSynchClearances.put(
                        SYNCH_Constants.PAYMENTS_RECEIVED_CATEGORY,
                        new List<SynchClearance__c>()
                    );
                }
                categoryToSynchClearances.get(SYNCH_Constants.PAYMENTS_RECEIVED_CATEGORY).add(sc);
            }
        }

        if (!categoryToSynchClearances.isEmpty()) {
            Map<String, List<SynchTarget__c>> categoryToSynchTargets = new Map<String, List<SynchTarget__c>>();

            for (SynchTarget__c target : [
                SELECT Id, QuarterStartDate__c, Category__c
                FROM SynchTarget__c
                WHERE Category__c IN :categoryToSynchClearances.keySet()
                ORDER BY QuarterStartDate__c
            ]) {
                if (!categoryToSynchTargets.containsKey(target.Category__c)) {
                    categoryToSynchTargets.put(target.Category__c, new List<SynchTarget__c>());
                }
                categoryToSynchTargets.get(target.Category__c).add(target);
            }

            if (categoryToSynchClearances.containsKey(SYNCH_Constants.CONFIRMED_SYNCHS_CATEGORY)) {
                for (SynchClearance__c sc : categoryToSynchClearances.get(SYNCH_Constants.CONFIRMED_SYNCHS_CATEGORY)) {
                    Id synchTargetId = findTargetIdByDate(
                        sc.DateOfConfirmation__c,
                        categoryToSynchTargets.get(SYNCH_Constants.CONFIRMED_SYNCHS_CATEGORY)
                    );
                    sc.ConfirmedSynchTarget__c = synchTargetId ?? null;
                }
            }

            if (categoryToSynchClearances.containsKey(SYNCH_Constants.PAYMENTS_RECEIVED_CATEGORY)) {
                for (SynchClearance__c sc : categoryToSynchClearances.get(SYNCH_Constants.PAYMENTS_RECEIVED_CATEGORY)) {
                    Id synchTargetId = findTargetIdByDate(
                        sc.PaymentReceivedDate__c,
                        categoryToSynchTargets.get(SYNCH_Constants.PAYMENTS_RECEIVED_CATEGORY)
                    );
                    sc.PaymentReceivedTarget__c = synchTargetId ?? null;
                }
            }
        }
    }

    /**
     * @description Find the right SynchTarget__c based on the provided synchDate
     *  SynchClearance__c record to the respective SynchTarget__c record
     * @param synchDate value of SynchClearance__c fields used to identify targets
     * @param targets list of targets to be included in the search
     */
    private static Id findTargetIdByDate(Date synchDate, List<SynchTarget__c> targets) {
        if (targets != null) {
            for (SynchTarget__c target : targets) {
                if (synchDate >= target.QuarterStartDate__c && synchDate < target.QuarterStartDate__c.addMonths(3)) {
                    return target.Id;
                }
            }
        }
        return null;
    }
}