package gov.va.med.edp.springframework.security.userdetails.vistalink;

import gov.va.med.edp.springframework.security.userdetails.VistaUserDetails;
import gov.va.med.edp.springframework.security.userdetails.VistaUserDetailsService;
import gov.va.med.edp.vistalink.ConnectionFactoryLocator;
import gov.va.med.edp.vistalink.VistaLinkDaoSupport;
import gov.va.med.edp.vistalink.VistaLinkTemplate;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.dao.DataAccessException;
import org.springframework.util.StringUtils;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;

public class VistaLinkUserDetailService extends VistaLinkDaoSupport implements VistaUserDetailsService {

    static final int DEFAULT_TIMEOUT = 600;

    static final String RPC_CONTEXT = "XUS KAAJEE WEB LOGON";

    static final String GET_USER_INFO_RPC = "XUS KAAJEE GET USER INFO";
    static final String LOGOUT_RPC_NAME = "XUS KAAJEE LOGOUT";

    private String applicationName;

    protected void checkDaoConfig() throws IllegalArgumentException {
        Assert.hasText(applicationName, "''applicationName' must not be empty");
        super.checkDaoConfig();
    }

    protected VistaLinkTemplate createRpcTemplate(ConnectionFactoryLocator connectionFactoryLocator) {
        VistaLinkTemplate template = super.createRpcTemplate(connectionFactoryLocator);
        template.setTimeOut(DEFAULT_TIMEOUT);
        return template;
    }

    public VistaUserDetails login(String stationNumber, String accessCode, String verifyCode, String remoteAddress) throws BadCredentialsException, DataAccessException {
        if (!StringUtils.hasLength(stationNumber)) throw new BadCredentialsException("missing station number");
        if (!StringUtils.hasLength(accessCode)) throw new BadCredentialsException("missing access code");
        if (!StringUtils.hasLength(verifyCode)) throw new BadCredentialsException("missing verify code");
        if (!StringUtils.hasLength(remoteAddress)) throw new BadCredentialsException("missing remote address");
        try {
            String result = getRpcTemplate().rpc(new VistaLinkAccessVerifyConnectionSpec(stationNumber, accessCode, verifyCode, remoteAddress), stationNumber, null, RPC_CONTEXT, GET_USER_INFO_RPC, createLoginParams(remoteAddress));
            return createVistaUserDetails(result, accessCode, verifyCode);
        } catch (DataAccessException e) {
            throw new BadCredentialsException("couldn't log in", e);
        }
    }

    public void logout(VistaUserDetails user) throws DataAccessException {
        getRpcTemplate().rpcAsUser(user.getLoginStationNumber(), user.getDuz(), RPC_CONTEXT, LOGOUT_RPC_NAME, createLogoutParams(user));
    }

    private List createLoginParams(String remoteAddress) {
        List params = new ArrayList();
        params.add(remoteAddress);
        params.add(getApplicationName());
        return params;
    }


    private List createLogoutParams(VistaUserDetails user) {
        List params = new ArrayList();
        params.add(user.getSignonLogInternalEntryNumber());
        return params;
    }

    /*
     * Result(0) is the users DUZ.
     * Result(1) is the user name from the .01 field.
     * Result(2) is the users full name from the name standard file.
     * Result(3) is the FAMILY (LAST) NAME (or ^ if null)
     * Result(4) is the GIVEN (FIRST) NAME (or ^ if null)
     * Result(5) is the MIDDLE NAME (or ^ if null)
     * Result(6) is the PREFIX (or ^ if null)
     * Result(7) is the SUFFIX (or ^ if null)
     * Result(8) is the DEGREE (or ^ if null)
     * Result(9) is station # of the division that the user is working in.
     * Result(10) is the station # of the parent facility for the login division
     * Result(11) is the station # of the computer system "parent" from the KSP file.
     * Result(12) is the IEN of the signon log entry
     * Result(13) = # of permissible divisions
     * Result(14-n) are the permissible divisions for user login, in the format:
     *             IEN of file 4^Station Name^Station Number^default? (1 or 0)
     */
    protected VistaUserDetails createVistaUserDetails(String result, String accessCode, String verifyCode) {
        String[] results = result.split("\n");
        VistaUser u = new VistaUser(results[12],
                results[9],
                results[0],
                accessCode,
                verifyCode,
                true,
                true,
                true,
                true,
                new GrantedAuthority[]{new GrantedAuthorityImpl("ROLE_USER")});
        u.setPersonName(results[1]);
        u.setDisplayName(results[2]);
        u.setFamilyName(nullSafeGet(results[3]));
        u.setGivenName(nullSafeGet(results[4]));
        u.setMiddleName(nullSafeGet(results[5]));
        u.setPrefix(nullSafeGet(results[6]));
        u.setSuffix(nullSafeGet(results[7]));
        u.setDegree(nullSafeGet(results[8]));
        return u;
    }

    private String nullSafeGet(String value) {
        if (value.equals("^")) return null;
        return value;
    }

    public String getApplicationName() {
        return applicationName;
    }

    public void setApplicationName(String applicationName) {
        this.applicationName = applicationName;
    }
}
