/***********************************************************************************************************
 * Name         : FormResponseWrapperProcessor
 * Purpose      : Class to validate and transform the FormResponseWrapper into a TFR/DO TFR record
 **********************************************************************************************************/

public with sharing class FormResponseWrapperProcessor {
    FormResponseWrapper messageWrapper;
    @TestVisible
    Map<String, Object> mapPropValues;
    @TestVisible
    Form__c form;

    public FormResponseWrapperProcessor(FormResponseWrapper messageWrapper) {
        this.messageWrapper = messageWrapper;
        this.mapPropValues = messageWrapper.deserialize();
    }

    /**
     * Method Name  : validateMessage
     * Description  : Validates the FormResponseWrapper
     * @return List<String> errors
     */
    public List<String> validateMessage() {
        List<String> errors = new List<String>();
        if (String.isBlank(messageWrapper.email_address)) {
            errors.add(UtilConstants.REST_RESPONSE_ERROR_EMPTY_EMAIL);
        } else {
            if (!messageWrapper.email_address.contains('@')) {
                errors.add(UtilConstants.REST_RESPONSE_ERROR_INVALID_EMAIL);
            } else {
                String email = messageWrapper.email_address;
                String emailDomain = '*@' + email.substringAfter('@');
                List<Blacklisted_Email__mdt> blacklistedEmail = [
                    SELECT Id
                    FROM Blacklisted_Email__mdt
                    WHERE Email__c = :email OR Email__c = :emailDomain
                    LIMIT 1
                ];
                if (!blacklistedEmail.isEmpty()) {
                    errors.add(UtilConstants.REST_RESPONSE_ERROR_BLACKLISTED_EMAIL);
                }
            }
        }

        if (String.isBlank(messageWrapper.form_id)) {
            errors.add(UtilConstants.REST_RESPONSE_ERROR_EMPTY_FORM);
        } else {
            getForm();
            if (form == null) {
                errors.add(UtilConstants.REST_RESPONSE_ERROR_INVALID_FORM);
            }
        }

        if (!UtilConstants.VALID_GENDERS.contains(messageWrapper.gender)) {
            errors.add(UtilConstants.REST_RESPONSE_ERROR_INVALID_GENDER);
        }

        if (
            !String.isBlank(messageWrapper.retargeting_consent) && Boolean.valueOf(messageWrapper.retargeting_consent)
        ) {
            List<String> retargetTerritories = System.Label.Retarget_Territories.split(',');
            if (!retargetTerritories.contains(form.TLA_ID__r.Territory_Id__r.Territory__c)) {
                errors.add(UtilConstants.REST_RESPONSE_ERROR_INVALID_RETARGET);
            }
        }

        if (messageWrapper.hasMailingLists()) {
            Set<String> receivedIds = new Set<String>(messageWrapper.getMailingListsIds());
            Set<String> validIds = new Set<String>();
            for (Mailing_List__c ml : [SELECT Id FROM Mailing_List__c WHERE Id IN :receivedIds]) {
                validIds.add(ml.Id);
            }
            if (receivedIds.size() != validIds.size()) {
                for (String mailingListId : receivedIds) {
                    if (!validIds.contains(mailingListId)) {
                        errors.add(
                            String.format(
                                UtilConstants.REST_RESPONSE_ERROR_INVALID_ML,
                                new List<Object>{ mailingListId }
                            )
                        );
                    }
                }
            }
        }
        return errors;
    }

    /**
     * Method Name  : getForm
     * Description  : Retrieves Form record associated to FormResponseWrapper
     * @return String record Id
     */
    @TestVisible
    private void getForm() {
        List<Form__c> forms = [
            SELECT
                ID,
                Form_Name__c,
                TLA_ID__c,
                TLA_ID__r.Territory_Id__c,
                TLA_ID__r.Territory_Id__r.Territory__c,
                Is_Double_Opt_In_Form__c,
                Migration_Id__c,
                Form_Tool_Name__c
            FROM Form__c
            WHERE Migration_Id__c = :messageWrapper.form_id
            LIMIT 1
        ];
        if (!forms.isEmpty()) {
            form = forms[0];
        }
    }

    /**
     * Method Name  : createTempFormResponseRecord
     * Description  : Transforms a FormResponseWrapper into a TFR/DO TFR record
     * @return String record Id
     */
    public List<String> createTempFormResponseRecord() {
        List<SObject> records = new List<SObject>();
        List<String> doTerritories = System.Label.Double_Opt_in_Exclusion_Territories.split(',');
        List<String> createdRecords = new List<String>();
        String doFormTerritory = form.TLA_ID__r.Territory_Id__r.Territory__c;
        Boolean shouldCreateTFR = true;

        if (
            form.Is_Double_Opt_In_Form__c &&
            messageWrapper.hasMailingLists() &&
            doTerritories.contains(doFormTerritory)
        ) {
            List<Subscription__c> doSubscriptions = [
                SELECT Id, Active__c, LastModifiedDate, LastModifiedBy.Username
                FROM Subscription__c
                WHERE
                    Fan_ID__r.Email__c = :messageWrapper.email_address
                    AND Mailing_List_ID__r.TLA_ID__r.Territory_ID__r.Territory__c = :doFormTerritory
                ORDER BY LastModifiedDate DESC
            ];

            if (doSubscriptions.isEmpty()) {
                shouldCreateTFR = false;
                createDOTFRs(records);
            } else {
                Boolean hasActiveSubscription = false;

                for (Subscription__c sub : doSubscriptions) {
                    if (sub.Active__c) {
                        hasActiveSubscription = true;
                        break;
                    }
                }

                if (!hasActiveSubscription) {
                    Subscription__c latestModifiedSub = doSubscriptions[0];
                    Date thresholdDate = Date.valueOf(ConfigUtils.getConfigString(ConfigUtils.DOI_THRESHOLD_DATE));
                    List<String> serviceUsers = ConfigUtils.getConfigMultipleStrings(ConfigUtils.DOI_USERS_TO_BYPASS);

                    if (
                        latestModifiedSub.LastModifiedDate < thresholdDate ||
                        !serviceUsers.contains(latestModifiedSub.LastModifiedBy.Username)
                    ) {
                        shouldCreateTFR = false;
                        createDOTFRs(records);
                    }
                }
            }
        }

        if (shouldCreateTFR) {
            Temp_Form_Response__c tfr = new Temp_Form_Response__c();
            assignCommonValues(tfr);
            tfr.put(
                'MailingList_Array__c',
                messageWrapper.mailing_list != null && !messageWrapper.mailing_list.isEmpty()
                    ? String.join(messageWrapper.getMailingListsIds(), ';')
                    : null
            );
            tfr.put(
                'TriggeredSends__c',
                messageWrapper.triggered_sends != null && !messageWrapper.triggered_sends.isEmpty()
                    ? String.join(messageWrapper.triggered_sends, ',')
                    : null
            );
            records.add(tfr);
        }

        insert records;

        for (SObject record : records) {
            createdRecords.add(record.Id);
        }
        return createdRecords;
    }

    /**
     * Method Name  : assignCommonValues
     * Description  : Assign values for fields common to TFR and DO TFR
     * @param SObject tfr, TFR / DO TFR object
     * @return void
     */
    @TestVisible
    private void assignCommonValues(SObject tfr) {
        Map<String, String> tfrCommonMapping = UtilConstants.FRW_COMMON_TFR_MAPPING;

        for (String property : tfrCommonMapping.keySet()) {
            System.debug(property + ' > ' + mapPropValues.get(property));
            tfr.put(tfrCommonMapping.get(property), mapPropValues.get(property));
        }

        //Form Fields
        tfr.put('Form_ID__c', form.Id);
        tfr.put('Is_Double_Opt_In_Form_Response__c', form.Is_Double_Opt_In_Form__c);
        tfr.put('Territory__c', form.TLA_ID__r.Territory_Id__r?.Territory__c);

        //Additional Fields
        tfr.put('Birthday__c', messageWrapper.date_of_birth);
        tfr.put(
            'Is_Retargeting_Form_Response__c',
            messageWrapper.retargeting_consent != null ? Boolean.valueOf(messageWrapper.retargeting_consent) : false
        );
        tfr.put('Source__c', String.isBlank(messageWrapper.source) ? form.Form_Tool_Name__c : messageWrapper.source);
        tfr.put('DSP__c', String.isBlank(messageWrapper.sourceChannel) ? 'Email' : messageWrapper.sourceChannel);
        tfr.put(
            'Preferred_Genre__c',
            String.isBlank(messageWrapper.music_affinity) ? null : messageWrapper.music_affinity.replace(',', ';')
        );
        //Custom Form Fields
        for (Integer i = 0; i < messageWrapper.custom_field?.size(); i++) {
            tfr.put('Custom_Form_Field' + String.valueOf(i + 1) + '__c', messageWrapper.custom_field.get(i));
        }
    }

    /**
     * Method Name  : createDOTFRs
     * Description  : Create the DO TFR records
     * @param List<SObject> list of SObjects to be populated
     * @return void
     */
    public void createDOTFRs(List<SObject> records) {
        for (String mailingList : messageWrapper.mailing_list) {
            DO_Temp_Form_Response__c doTfr = new DO_Temp_Form_Response__c();
            assignCommonValues(doTfr);
            doTfr.put('MailingList__c', Id.valueOf(mailingList));
            doTFr.put('Form_Migration_ID__c', messageWrapper.form_id);
            records.add(doTfr);
        }
    }
}
