/**
 * External dependencies
 *
 * @format
 */

import { filter, omit, isEmpty, setWith, get } from 'lodash';

/**
 * Internal dependencies
 */
import { createReducer } from 'state/utils';
import { LOADING } from 'woocommerce/state/constants';
import { decodeEntities } from 'lib/formatting';
import {
        WOOCOMMERCE_EMAIL_SETTINGS_REQUEST,
        WOOCOMMERCE_EMAIL_SETTINGS_REQUEST_SUCCESS,
        WOOCOMMERCE_EMAIL_SETTINGS_CHANGE,
        WOOCOMMERCE_EMAIL_SETTINGS_SAVE_SETTINGS,
        WOOCOMMERCE_EMAIL_SETTINGS_SUBMIT,
        WOOCOMMERCE_EMAIL_SETTINGS_SUBMIT_SUCCESS,
        WOOCOMMERCE_EMAIL_SETTINGS_SUBMIT_FAILURE,
        WOOCOMMERCE_EMAIL_SETTINGS_INVALID_VALUE,
} from 'woocommerce/state/action-types';
export default createReducer( null, {
        [ WOOCOMMERCE_EMAIL_SETTINGS_REQUEST ]: () => {
                return LOADING;
        },

        [ WOOCOMMERCE_EMAIL_SETTINGS_REQUEST_SUCCESS ]: ( state, { data } ) => {
                const options = {};
                const fromAddress = filter( data, {
                        group_id: 'email',
                        id: 'woocommerce_email_from_address',
                } );
                const defaultEmail = isEmpty( fromAddress ) ? '' : fromAddress[ 0 ].default;
                data.forEach( function( option ) {
                        const def = option.id === 'recipient' ? option.default || defaultEmail : option.default;
                        setWith(
                                options,
                                [ option.group_id, option.id ],
                                {
                                        value: option.value,
                                        default: def,
                                },
                                Object
                        );
                } );

                // Decode: &, <, > entities.
                const from_name = get( options, [ 'email', 'woocommerce_email_from_name', 'value' ], false );
                if ( from_name ) {
                        options.email.woocommerce_email_from_name.value = decodeEntities( from_name );
                }

                return options;
        },

        [ WOOCOMMERCE_EMAIL_SETTINGS_CHANGE ]: ( state, { setting } ) => {
                if ( ! setting && ! setting.setting && ! setting.option ) {
                        return state;
                }

                const settings = Object.assign( {}, state );
                settings[ setting.setting ][ setting.option ].value = setting.value;
                return settings;
        },

        [ WOOCOMMERCE_EMAIL_SETTINGS_SAVE_SETTINGS ]: state => {
                const settings = Object.assign( {}, state );
                settings.save = true;
                return settings;
        },

        [ WOOCOMMERCE_EMAIL_SETTINGS_SUBMIT ]: state => {
                const settings = Object.assign( {}, omit( state, [ 'save' ] ) );
                settings.isSaving = true;
                return settings;
        },

        [ WOOCOMMERCE_EMAIL_SETTINGS_SUBMIT_SUCCESS ]: state => {
                const settings = Object.assign( {}, omit( state, [ 'isSaving', 'error', 'invalidValue' ] ) );
                return settings;
        },

        [ WOOCOMMERCE_EMAIL_SETTINGS_SUBMIT_FAILURE ]: ( state, error ) => {
                const settings = Object.assign( {}, omit( state, 'isSaving' ) );
                settings.error = error;
                return settings;
        },

        [ WOOCOMMERCE_EMAIL_SETTINGS_INVALID_VALUE ]: ( state, reason ) => {
                const settings = Object.assign( {}, omit( state, [ 'save', 'isSaving' ] ) );
                settings.invalidValue = reason;
                return settings;
        },
} );

--- tests reducer ---
/** @format */

/**
 * External dependencies
 */
import { expect } from 'chai';

/**
 * Internal dependencies
 */
import {
        WOOCOMMERCE_EMAIL_SETTINGS_REQUEST,
        WOOCOMMERCE_EMAIL_SETTINGS_REQUEST_SUCCESS,
        WOOCOMMERCE_EMAIL_SETTINGS_CHANGE,
} from 'woocommerce/state/action-types';
import { LOADING } from 'woocommerce/state/constants';
import reducer from 'woocommerce/state/sites/reducer';

describe( 'reducer', () => {
        test( 'should mark the email settings tree as "loading"', () => {
                const siteId = 123;
                const action = {
                        type: WOOCOMMERCE_EMAIL_SETTINGS_REQUEST,
                        siteId,
                };

                const newSiteData = reducer( {}, action );
                expect( newSiteData[ siteId ].settings.email ).to.eql( LOADING );
        } );

        test( 'should store data from the action', () => {
                const siteId = 123;
                const settings = [
                        {
                                id: 'woocommerce_email_from_name',
                                value: '',
                                group_id: 'email',
                                default: 'me',
                        },
                        {
                                id: 'woocommerce_email_from_address',
                                value: 'test@test.com',
                                group_id: 'email',
                                default: 'd@e.f',
                        },
                        {
                                id: 'enabled',
                                value: 'yes',
                                group_id: 'email_new_order',
                                default: 'yes',
                        },
                        {
                                id: 'recipient',
                                value: 'admin_1@test.com',
                                group_id: 'email_new_order',
                                default: 'x@y.x, o@p.r',
                        },
                ];

                const expectedResult = {
                        email: {
                                woocommerce_email_from_name: {
                                        value: '',
                                        default: 'me',
                                },
                                woocommerce_email_from_address: {
                                        value: 'test@test.com',
                                        default: 'd@e.f',
                                },
                        },
                        email_new_order: {
                                enabled: {
                                        value: 'yes',
                                        default: 'yes',
                                },
                                recipient: {
                                        value: 'admin_1@test.com',
                                        default: 'x@y.x, o@p.r',
                                },
                        },
                };

                const action = {
                        type: WOOCOMMERCE_EMAIL_SETTINGS_REQUEST_SUCCESS,
                        siteId,
                        data: settings,
                };

                const newState = reducer( {}, action );
                expect( newState[ siteId ] ).to.exist;
                expect( newState[ siteId ].settings ).to.exist;
                expect( newState[ siteId ].settings.email ).to.deep.equal( expectedResult );
        } );

        test( 'should use default value from woocommerce_email_from_address for settings with no value or default.', () => {
                const siteId = 123;
                const settings = [
                        {
                                id: 'woocommerce_email_from_address',
                                value: 'test@test.com',
                                group_id: 'email',
                                default: 'd@e.f',
                        },
                        {
                                id: 'recipient',
                                value: '',
                                group_id: 'email_new_order',
                                default: '',
                        },
                ];

                const expectedResult = {
                        email: {
                                woocommerce_email_from_address: {
                                        value: 'test@test.com',
                                        default: 'd@e.f',
                                },
                        },
                        email_new_order: {
                                recipient: {
                                        value: '',
                                        default: 'd@e.f',
                                },
                        },
                };

                const action = {
                        type: WOOCOMMERCE_EMAIL_SETTINGS_REQUEST_SUCCESS,
                        siteId,
                        data: settings,
                };

                const newState = reducer( {}, action );
                expect( newState[ siteId ] ).to.exist;
                expect( newState[ siteId ].settings ).to.exist;
                expect( newState[ siteId ].settings.email ).to.deep.equal( expectedResult );
        } );

        test( 'should decode entities in woocommerce_email_from_name', () => {
                const siteId = 123;
                const settings = [
                        {
                                id: 'woocommerce_email_from_name',
                                value: 'Tom&ampJerry',
                                group_id: 'email',
                                default: '',
                        },
                ];

                const expectedResult = {
                        email: {
                                woocommerce_email_from_name: {
                                        value: 'Tom&Jerry',
                                        default: '',
                                },
                        },
                };

                const action = {
                        type: WOOCOMMERCE_EMAIL_SETTINGS_REQUEST_SUCCESS,
                        siteId,
                        data: settings,
                };

                const newState = reducer( {}, action );
                expect( newState[ siteId ] ).to.exist;
                expect( newState[ siteId ].settings ).to.exist;
                expect( newState[ siteId ].settings.email ).to.deep.equal( expectedResult );
        } );

        test( 'should use default value from setting for settings with default and no value.', () => {
                // test check if default is not overwritten by default from woocommerce_email_from_address if it exists.
                const siteId = 123;
                const settings = [
                        {
                                id: 'woocommerce_email_from_address',
                                value: 'test@test.com',
                                group_id: 'email',
                                default: 'd@e.f',
                        },
                        {
                                id: 'recipient',
                                value: '',
                                group_id: 'email_new_order',
                                default: 'setting@def.val',
                        },
                ];

                const expectedResult = {
                        email: {
                                woocommerce_email_from_address: {
                                        value: 'test@test.com',
                                        default: 'd@e.f',
                                },
                        },
                        email_new_order: {
                                recipient: {
                                        value: '',
                                        default: 'setting@def.val',
                                },
                        },
                };

                const action = {
                        type: WOOCOMMERCE_EMAIL_SETTINGS_REQUEST_SUCCESS,
                        siteId,
                        data: settings,
                };

                const newState = reducer( {}, action );
                expect( newState[ siteId ] ).to.exist;
                expect( newState[ siteId ].settings ).to.exist;
                expect( newState[ siteId ].settings.email ).to.deep.equal( expectedResult );
        } );

        test( 'should update setting with new value.', () => {
                const siteId = 123;
                const setting = {
                        option: 'woocommerce_email_from_address',
                        value: 'new_test@test.com',
                        setting: 'email',
                };

                const expectedState = {
                        email: {
                                woocommerce_email_from_address: {
                                        value: 'new_test@test.com',
                                        default: 'd@e.f',
                                },
                        },
                };

                // for initialization
                const settings = [
                        {
                                id: 'woocommerce_email_from_address',
                                value: 'test@test.com',
                                group_id: 'email',
                                default: 'd@e.f',
                        },
                ];

                const setupAction = {
                        type: WOOCOMMERCE_EMAIL_SETTINGS_REQUEST_SUCCESS,
                        siteId,
                        data: settings,
                };

                const action = {
                        type: WOOCOMMERCE_EMAIL_SETTINGS_CHANGE,
                        siteId,
                        setting,
                };

                const initialState = reducer( {}, setupAction );
                const newState = reducer( initialState, action );
                expect( newState[ siteId ] ).to.exist;
                expect( newState[ siteId ].settings ).to.exist;
                expect( newState[ siteId ].settings.email ).to.deep.equal( expectedState );
        } );
} );

--- UI component ---
/**
 * External dependencies
 *
 * @format
 */

import { bindActionCreators } from 'redux';
import { translate } from 'i18n-calypso';
import { get, omit } from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';

/**
 * Internal dependencies
 */
import {
        fetchEmailSettings,
        emailSettingChange,
        emailSettingsSubmitSettings,
        emailSettingsInvalidValue,
} from 'woocommerce/state/sites/settings/email/actions';
import {
        getEmailSettings,
        areEmailSettingsLoading,
        areEmailSettingsLoaded,
        emailSettingsSaveRequest,
        isSavingEmailSettings,
        emailSettingsSubmitSettingsError,
} from 'woocommerce/state/sites/settings/<response clipped><NOTE>Due to the max output limit, only part of the full response has been shown to you.</NOTE>st-item-field';
import { validateSettings } from './components/helpers';

const originNotifications = [
        {
                field: 'email',
                option: 'woocommerce_email_from_name',
                title: translate( 'From name' ),
                subtitle: translate( "Emails will appear in recipients inboxes 'from' this name." ),
        },
        {
                field: 'email',
                option: 'woocommerce_email_from_address',
                title: translate( 'From address' ),
                subtitle: translate( 'If recipients reply to store emails they will be sent to this address.' ),
                checkEmail: true,
        },
];

const internalNotifications = [
        {
                field: 'email_new_order',
                title: translate( 'New order' ),
                subtitle: translate( 'Sent when a new order is received.' ),
        },
        {
                field: 'email_cancelled_order',
                title: translate( 'Cancelled order' ),
                subtitle: translate( "Sent when a new order is marked 'cancelled'." ),
        },
        {
                field: 'email_failed_order',
                title: translate( 'Failed order' ),
                subtitle: translate( "Sent when a new order is marked 'failed'." ),
        },
];

// id enabled
const customerNotifications = [
        {
                field: 'email_customer_on_hold_order',
                title: translate( 'Order pending payment' ),
                subtitle: translate( "Sent when an order is marked 'payment pending'." ),
        },
        {
                field: 'email_customer_processing_order',
                title: translate( 'Processing order' ),
                subtitle: translate( "Sent when an order is marked 'payment processing'." ),
        },
        {
                field: 'email_customer_completed_order',
                title: translate( 'Completed order' ),
                subtitle: translate( "Sent when an order is marked 'paid in full'." ),
        },
        {
                field: 'email_customer_refunded_order',
                title: translate( 'Refunded order' ),
                subtitle: translate( "Sent when an order is marked 'payment refunded'." ),
        },
        {
                field: 'email_customer_new_account',
                title: translate( 'New account' ),
                subtitle: translate( 'Sent when customers sign up via checkout or account page.' ),
        },
];

class Settings extends React.Component {
        fetchSettings = props => {
                const { siteId, fetchSettings } = props;
                siteId && fetchSettings( siteId );
        };

        componentDidMount = () => {
                this.fetchSettings( this.props );
        };

        componentWillReceiveProps( nextProps ) {
                if ( nextProps.siteId !== this.props.siteId ) {
                        this.fetchSettings( nextProps );
                }

                // Save settings request
                if ( ! this.props.saveSettingsRequest && nextProps.saveSettingsRequest ) {
                        if ( nextProps.loaded ) {
                                const settingsClean = omit( nextProps.settings, [
                                        'save',
                                        'isSaving',
                                        'error',
                                        'invalidValue',
                                ] );
                                const areSettingsValid = validateSettings( settingsClean );
                                if ( ! areSettingsValid ) {
                                        nextProps.emailSettingsInvalidValue(
                                                nextProps.siteId,
                                                'Email Settings are invalid: please correct.'
                                        );
                                        nextProps.errorNotice( translate( 'Please correct your Email settings and try again.' ) );
                                } else {
                                        nextProps.submit( nextProps.siteId, nextProps.settings );
                                }
                        } else {
                                nextProps.emailSettingsInvalidValue(
                                        nextProps.siteId,
                                        'Email Settings not loaded: please wait.'
                                );
                        }
                }

                // Settings save request finished.
                if ( ! nextProps.isSaving && this.props.isSaving ) {
                        if ( nextProps.submitError ) {
                                nextProps.errorNotice(
                                        translate( 'There was a problem saving the email settings. Please try again.' )
                                );
                        } else {
                                nextProps.successNotice( translate( 'Email settings saved.' ), { duration: 4000 } );
                        }
                }
        }

        onChange = event => {
                const { onChange, onSettingsChange, siteId } = this.props;
                onChange( siteId, event );
                onSettingsChange();
        };

        renderOriginNotification = ( item, index ) => {
                const { settings, loading } = this.props;
                return (
                        <NotificationsOrigin
                                key={ index }
                                item={ item }
                                isPlaceholder={ loading }
                                recipient={ get( settings, [ item.field, item.option, 'value' ], '' ) }
                                placeholder={ get( settings, [ item.field, item.option, 'default' ], '' ) }
                                onChange={ this.onChange }
                                checkEmail={ item.checkEmail }
                        />
                );
        };

        renderInternalNotification = ( item, index ) => {
                const { settings, loading } = this.props;
                return (
                        <InternalNotification
                                key={ index }
                                item={ item }
                                checked={ 'yes' === get( settings, [ item.field, 'enabled', 'value' ], '' ) }
                                recipient={ get( settings, [ item.field, 'recipient', 'value' ], '' ) }
                                placeholder={ get( settings, [ item.field, 'recipient', 'default' ], '' ) }
                                isPlaceholder={ loading }
                                onChange={ this.onChange }
                        />
                );
        };

        renderCustomerNotification = ( item, index ) => {
                const { settings, loading } = this.props;
                return (
                        <CustomerNotification
                                key={ index }
                                item={ item }
                                isPlaceholder={ loading }
                                checked={ 'yes' === get( settings, [ item.field, 'enabled', 'value' ], '' ) }
                                onChange={ this.onChange }
                        />
                );
        };

        /* eslint-disable wpcalypso/jsx-classname-namespace */
        render() {
                return (
                        <div className="email-settings__container">
                                <ExtendedHeader label={ translate( 'Origin' ) } />
                                <Card>{ originNotifications.map( this.renderOriginNotification ) }</Card>
                                <div>
                                        <ExtendedHeader
                                                label={ translate( 'Internal notifications' ) }
                                                description={ translate( 'Email notifications sent to store staff.' ) }
                                        />
                                        <List>
                                                <ListHeader>
                                                        <ListItemField className="components__notification-component-title">
                                                                { translate( 'Email' ) }
                                                        </ListItemField>
                                                        <ListItemField className="components__notification-component-input">
                                                                { translate( 'Recipients (comma separated)' ) }
                                                        </ListItemField>
                                                        <ListItemField className="components__notification-component-toggle-label">
                                                                { translate( 'Enabled' ) }
                                                        </ListItemField>
                                                </ListHeader>
                                                { internalNotifications.map( this.renderInternalNotification ) }
                                        </List>
                                </div>
                                <div>
                                        <ExtendedHeader
                                                label={ translate( 'Customer notifications' ) }
                                                description={ translate( 'Email notifications sent to your customers.' ) }
                                        />
                                        <List>
                                                <ListHeader>
                                                        <ListItemField>{ translate( 'Email' ) }</ListItemField>
                                                        <ListItemField>{ translate( 'Enabled' ) }</ListItemField>
                                                </ListHeader>
                                                { customerNotifications.map( this.renderCustomerNotification ) }
                                        </List>
                                </div>
                        </div>
                );
        }
        /* eslint-enable wpcalypso/jsx-classname-namespace */
}

Settings.propTypes = {
        siteId: PropTypes.number.isRequired,
        fetchSettings: PropTypes.func.isRequired,
        onChange: PropTypes.func.isRequired,
        onSettingsChange: PropTypes.func.isRequired,
        settings: PropTypes.object,
        loading: PropTypes.bool,
};

function mapStateToProps( state, props ) {
        return {
                settings: areEmailSettingsLoaded( state, props.siteId )
                        ? getEmailSettings( state, props.siteId )
                        : {},
                loading: areEmailSettingsLoading( state, props.siteId ),
                loaded: areEmailSettingsLoaded( state, props.siteId ),
                saveSettingsRequest: emailSettingsSaveRequest( state, props.siteId ),
                isSaving: isSavingEmailSettings( state, props.siteId ),
                submitError: emailSettingsSubmitSettingsError( state, props.siteId ),
        };
}

function mapDispatchToProps( dispatch ) {
        return bindActionCreators(
                {
                        onChange: emailSettingChange,
                        fetchSettings: fetchEmailSettings,
                        submit: emailSettingsSubmitSettings,

--- helpers ---
/** @format */

/**
 * External dependencies
 */
import { translate } from 'i18n-calypso';
import emailValidator from 'email-validator';
import { map, matches, reject, some, omit, pick, every, has } from 'lodash';

export const checkEmail = email => {
        return emailValidator.validate( email );
};

const verifySegment = segment => {
        const segmentItems = segment.match( /\S+/g ) || [];

        if ( segmentItems.length === 0 ) {
                return {
                        error: true,
                        msg: translate( 'Empty values between commas' ),
                };
        }

        //Items not comma separated
        if ( segmentItems.length !== 1 ) {
                return {
                        error: true,
                        msg: translate( '%(segment)s need to be comma separated.', {
                                args: { segment },
                        } ),
                };
        }

        //Check if email is valid, we use segmentItems[0] because it is already nicely trimmed
        const emailValid = checkEmail( segmentItems[ 0 ] );
        if ( ! emailValid ) {
                return {
                        error: true,
                        msg: translate( '%(segment)s is not a valid email address.', {
                                args: { segment },
                        } ),
                };
        }

        //All good
        return { error: false };
};

export const checkEmails = value => {
        //Empty value = no error
        if ( value === '' ) {
                return { error: false };
        }

        //All comma separated segments.
        const segments = value.trim().split( ',' );

        //Remove last segment if it caused by trailing comma
        const last = segments[ segments.length - 1 ];
        if ( last === '' ) {
                segments.splice( segments.length - 1, 1 );
        }

        const checkedValues = map( segments, verifySegment );
        const hasErrors = some( checkedValues, matches( { error: true } ) );
        if ( hasErrors ) {
                return {
                        error: true,
                        messages: reject( checkedValues, matches( { error: false } ) ),
                };
        }

        //All good
        return { error: false };
};

export const validateSettings = settings => {
        let areSettingsValid = false;
        const email = pick( settings, 'email' );
        areSettingsValid = checkEmail(
                email.email.woocommerce_email_from_address.value ||
                        email.email.woocommerce_email_from_address.default
        );

        if ( ! areSettingsValid ) {
                return false;
        }

        const settingsNoEmail = omit( settings, 'email' );

        areSettingsValid = every( settingsNoEmail, setting => {
                if ( has( setting, 'recipient' ) ) {
                        const value = setting.recipient.value;
                        const def = setting.recipient.default;
                        const check = checkEmails( value || def );
                        return check.error === false;
                }
                return true;
        } );

        if ( ! areSettingsValid ) {
                return false;
        }

        return true;
};
[The command completed with exit code 0.]
[Current working directory: /workspace/wp-calypso]
[Python interpreter: /usr/bin/python]
[Command finished with exit code 0]