/**
 * This file implements authentication related actions.
 */

// NPM imports
import { push } from "react-router-redux";
import Cookies from "js-cookie";

// Local imports
import { buildHMAC, cleanURL } from "../utils";

// Models
import { i18nRecord } from "../models/i18n";

// Other actions and payload types
import { CALL_API } from "../middleware/api";
import { invalidateStore } from "./store";


// Constants
export const DEFAULT_SESSION_INTERVAL = 1800 * 1000;  // 30 mins long sessoins by default


/**
 * Dispatch a ping query to the API for login keepalive and prevent session
 * from expiring.
 *
 * @param   username    Username to use
 * @param   token       Token to revive
 * @param   endpoint    Ampache base URL
 *
 * @return A CALL_API payload to keep session alive.
 */
export function loginKeepAlive(username, token, endpoint) {
    return {
        type: CALL_API,
        payload: {
            endpoint: endpoint,
            dispatch: [
                null,
                null,
                error => dispatch => {
                    dispatch(loginUserFailure(error || new i18nRecord({ id: "app.login.expired", values: {}})));
                },
            ],
            action: "ping",
            auth: token,
            username: username,
            extraParams: {},
        },
    };
}


export const LOGIN_USER_SUCCESS = "LOGIN_USER_SUCCESS";
/**
 * Action to be called on successful login.
 *
 * @param   username    Username used for login
 * @param   token       Token got back from the API
 * @param   endpoint    Ampache server base URL
 * @param   rememberMe  Whether to remember me or not
 * @param   timerID     ID of the timer set for session keepalive.
 *
 * @return A login success payload.
 */
export function loginUserSuccess(username, token, endpoint, rememberMe, timerID) {
    return {
        type: LOGIN_USER_SUCCESS,
        payload: {
            username: username,
            token: token,
            endpoint: endpoint,
            rememberMe: rememberMe,
            timerID: timerID,
        },
    };
}


export const LOGIN_USER_FAILURE = "LOGIN_USER_FAILURE";
/**
 * Action to be called on failed login.
 *
 * This action removes any remember me cookie if any was set.
 *
 * @param   error   An error object, either string or i18nRecord.
 * @return  A login failure payload.
 */
export function loginUserFailure(error) {
    Cookies.remove("username");
    Cookies.remove("token");
    Cookies.remove("endpoint");
    return {
        type: LOGIN_USER_FAILURE,
        payload: {
            error: error,
        },
    };
}


export const LOGIN_USER_EXPIRED = "LOGIN_USER_EXPIRED";
/**
 * Action to be called when session is expired.
 *
 * @param   error   An error object, either a string or i18nRecord.
 * @return  A session expired payload.
 */
export function loginUserExpired(error) {
    return {
        type: LOGIN_USER_EXPIRED,
        payload: {
            error: error,
        },
    };
}


export const LOGIN_USER_REQUEST = "LOGIN_USER_REQUEST";
/**
 * Action to be called when login is requested.
 *
 * @return  A login request payload.
 */
export function loginUserRequest() {
    return {
        type: LOGIN_USER_REQUEST,
    };
}


export const LOGOUT_USER = "LOGOUT_USER";
/**
 * Action to be called upon logout.
 *
 * This function clears the cookies set for remember me and the keep alive
 * timer.
 *
 * @remark  This function does not clear the other stores, nor handle
 *          redirection.
 *
 * @return  A logout payload.
 */
export function logout() {
    return (dispatch, state) => {
        const { auth } = state();
        if (auth.timerID) {
            clearInterval(auth.timerID);
        }
        Cookies.remove("username");
        Cookies.remove("token");
        Cookies.remove("endpoint");
        dispatch({
            type: LOGOUT_USER,
        });
    };
}


/**
 * Action to be called to log a user out.
 *
 * This function clears the remember me cookies and the keepalive timer. It
 * also clears the data behind authentication in the store and redirects to
 * login page.
 */
export function logoutAndRedirect() {
    return (dispatch) => {
        dispatch(logout());
        dispatch(invalidateStore());
        dispatch(push("/login"));
    };
}


/**
 * Action to be called to log a user in.
 *
 * @param   username    Username to use.
 * @param   passwordOrToken     User password, or previous token to revive.
 * @param   endpoint    Ampache server base URL.
 * @param   rememberMe  Whether to rememberMe or not
 * @param[optional]     redirect    Page to redirect to after login.
 * @param[optional]     isToken     Whether passwordOrToken is a password or a
 *                                  token.
 *
 * @return  A CALL_API payload to perform login.
 */
export function loginUser(username, passwordOrToken, endpoint, rememberMe, redirect="/", isToken=false) {
    // Clean endpoint
    endpoint = cleanURL(endpoint);

    // Get passphrase and time parameters
    let time = 0;
    let passphrase = passwordOrToken;
    if (!isToken) {
        // Standard password connection
        const HMAC = buildHMAC(passwordOrToken);
        time = HMAC.time;
        passphrase = HMAC.passphrase;
    } else {
        // Remember me connection
        if (passwordOrToken.expires < new Date()) {
            // Token has expired
            return loginUserFailure("app.login.expired");
        }
        time = Math.floor(Date.now() / 1000);
        passphrase = passwordOrToken.token;
    }

    return {
        type: CALL_API,
        payload: {
            endpoint: endpoint,
            dispatch: [
                loginUserRequest,
                jsonData => dispatch => {
                    if (!jsonData.auth || !jsonData.sessionExpire) {
                        // On success, check that we are actually authenticated
                        return dispatch(loginUserFailure(new i18nRecord({ id: "app.api.error", values: {} })));
                    }
                    // Get token from the API
                    const token = {
                        token: jsonData.auth,
                        expires: new Date(jsonData.sessionExpire),
                    };
                    // Handle session keep alive timer
                    const timerID = setInterval(
                        () => dispatch(loginKeepAlive(username, token.token, endpoint)),
                        DEFAULT_SESSION_INTERVAL
                    );
                    if (rememberMe) {
                        // Handle remember me option
                        const cookiesOption = { expires: token.expires };
                        Cookies.set("username", username, cookiesOption);
                        Cookies.set("token", token, cookiesOption);
                        Cookies.set("endpoint", endpoint, cookiesOption);
                    }
                    // Dispatch login success
                    dispatch(loginUserSuccess(username, token, endpoint, rememberMe, timerID));
                    // Redirect
                    dispatch(push(redirect));
                },
                loginUserFailure,
            ],
            action: "handshake",
            auth: passphrase,
            username: username,
            extraParams: {timestamp: time},
        },
    };
}
