package com.braintree.security;

import de.hybris.platform.acceleratorstorefrontcommons.security.AbstractAcceleratorAuthenticationProvider;
import de.hybris.platform.acceleratorstorefrontcommons.security.BruteForceAttackCounter;
import de.hybris.platform.core.Constants;
import de.hybris.platform.core.model.user.UserModel;
import de.hybris.platform.jalo.JaloSession;
import de.hybris.platform.jalo.user.User;
import de.hybris.platform.jalo.user.UserManager;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.user.UserService;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;

public class PayPalAuthenticationProvider extends AbstractAcceleratorAuthenticationProvider {
    private static final Logger LOG = Logger.getLogger(PayPalAuthenticationProvider.class);

    private static final String CORE_AUTHENTICATION_PROVIDER_BAD_CREDENTIALS = "CoreAuthenticationProvider.badCredentials";
    private static final String BAD_CREDENTIALS = "Bad credentials";

    private BruteForceAttackCounter bruteForceAttackCounter;
    private UserService userService;
    private ModelService modelService;

    @Override
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
        final String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
        final boolean isBruteForceAttack = bruteForceAttackCounter.isAttack(username);
        UserModel userModel = getUserService().getUserForUID(StringUtils.lowerCase(username));

        if (userModel.isLoginDisabled()) {
            LOG.info("Skipping authentication. User's login is disabled");
            bruteForceAttackCounter.resetUserCounter(userModel.getUid());
            throw new BadCredentialsException(messages.getMessage(CORE_AUTHENTICATION_PROVIDER_BAD_CREDENTIALS, BAD_CREDENTIALS));
        }

        if (isBruteForceAttack) {
            userModel.setLoginDisabled(true);
            getModelService().save(userModel);
            bruteForceAttackCounter.resetUserCounter(userModel.getUid());
            throw new BadCredentialsException(messages.getMessage(CORE_AUTHENTICATION_PROVIDER_BAD_CREDENTIALS, BAD_CREDENTIALS));
        }

        if (!getUserService().isMemberOfGroup(userModel, getUserService().getUserGroupForUID(Constants.USER.CUSTOMER_USERGROUP))) {
            throw new BadCredentialsException(messages.getMessage(CORE_AUTHENTICATION_PROVIDER_BAD_CREDENTIALS, BAD_CREDENTIALS));
        }

        UserDetails userDetails = this.retrieveUser(username);
        this.getPreAuthenticationChecks().check(userDetails);

        User user = UserManager.getInstance().getUserByLogin(userDetails.getUsername());
        JaloSession.getCurrentSession().setUser(user);

        ConnectWithPayPalAuthenticationToken result = new ConnectWithPayPalAuthenticationToken(username, userDetails.getAuthorities());
        result.setDetails(authentication.getDetails());
        return result;
    }

    @Override
    public boolean supports(Class authentication) {
        return ConnectWithPayPalAuthenticationToken.class.isAssignableFrom(authentication);
    }

    protected BruteForceAttackCounter getBruteForceAttackCounter() {
        return bruteForceAttackCounter;
    }

    @Required
    public void setBruteForceAttackCounter(final BruteForceAttackCounter bruteForceAttackCounter) {
        this.bruteForceAttackCounter = bruteForceAttackCounter;
    }

    protected UserService getUserService() {
        return userService;
    }

    @Required
    public void setUserService(final UserService userService) {
        this.userService = userService;
    }

    protected ModelService getModelService() {
        return modelService;
    }

    @Required
    public void setModelService(final ModelService modelService) {
        this.modelService = modelService;
    }
}