"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthenticationContextProvider = void 0;
const contexts_1 = require("../../index.js");
const base_64_1 = __importDefault(require("base-64"));
const react_1 = __importStar(require("react"));
const MemberPortalFeature_1 = __importDefault(require("../../../providers/feature/MemberPortalFeature"));
const AsyncAuth_1 = require("../../../util/AsyncAuth");
const DataLayer_1 = __importDefault(require("../../../util/DataLayer"));
const useAuthenticationContext = () => {
    const { companyId, companyShort, selectedHotelID, colors, logoLink = '', name: companyName, logo: companyLogo } = (0, react_1.useContext)(contexts_1.CompanyContext);
    const clientMetadata = (0, react_1.useMemo)(() => ({
        hotelId: selectedHotelID || '0',
        companyShort: companyShort,
    }), [selectedHotelID, companyShort]);
    const [pendingCredentials, setPendingCredentials] = (0, react_1.useState)(null);
    const [finishedInitializingContext, finishedInitializingContextSet] = (0, react_1.useState)(false);
    const [isFetching, isFetchingSet] = (0, react_1.useState)(true);
    const loading = (0, react_1.useMemo)(() => {
        return !finishedInitializingContext || isFetching;
    }, [finishedInitializingContext, isFetching]);
    const [cognitoUser, cognitoUserSet] = (0, react_1.useState)(null);
    const [cognitoLoginState, cognitoLoginStateSet] = (0, react_1.useState)(contexts_1.CognitoLoginState.Login);
    const [error, errorSet] = (0, react_1.useState)();
    /**
     * Set up the context, and try and immediately log in the user in the event that they're here
     */
    const initialiseContext = (0, react_1.useCallback)(() => __awaiter(void 0, void 0, void 0, function* () {
        const Auth = yield (0, AsyncAuth_1.getAuth)();
        yield Auth.currentAuthenticatedUser()
            .then((user) => {
            if (user) {
                const [userCompanyShort] = base_64_1.default.decode(user.username).split('-');
                if (userCompanyShort === companyShort) {
                    cognitoUserSet(user);
                    cognitoLoginStateSet(contexts_1.CognitoLoginState.Success);
                    handleSignInSuccess(user);
                }
            }
        })
            .catch((e) => {
            if (e !== 'The user is not authenticated') {
                errorSet(e);
            }
        });
        finishedInitializingContextSet(true);
        isFetchingSet(false);
    }), []);
    (0, react_1.useEffect)(() => {
        if (MemberPortalFeature_1.default.isActive()) {
            initialiseContext();
        }
    }, [initialiseContext, MemberPortalFeature_1.default.isActive()]);
    /**
     * Wrapper for our async calls to handle the `isFetching` and `error` state
     */
    const asyncWrapper = (func, ...parameters) => __awaiter(void 0, void 0, void 0, function* () {
        isFetchingSet(true);
        errorSet(undefined);
        try {
            return yield func(...parameters);
        }
        catch (e) {
            errorSet(e);
        }
        finally {
            isFetchingSet(false);
        }
    });
    const transformUsername = (0, react_1.useCallback)((username) => {
        return base_64_1.default.encode(`${companyShort}-${username.toLowerCase()}`);
    }, [companyShort]);
    const handleSignInSuccess = (user) => {
        DataLayer_1.default.instance.setUserId(user.attributes.sub).sendMemberSignedIn();
    };
    const handleSignUpSuccess = () => {
        DataLayer_1.default.instance.sendMemberSignUp();
    };
    const login = (email, password, extraClientMetadataParams) => __awaiter(void 0, void 0, void 0, function* () {
        const Auth = yield (0, AsyncAuth_1.getAuth)();
        let cState;
        const username = transformUsername(email);
        try {
            const cu = yield Auth.signIn({
                username,
                password,
            });
            if (cu.challengeName === 'SMS_MFA' || cu.challengeName === 'SOFTWARE_TOKEN_MFA') {
                // TODO: Handle 2FA
                cState = contexts_1.CognitoLoginState.VerifyMFA;
            }
            else if (cu.challengeName === 'NEW_PASSWORD_REQUIRED') {
                // TODO: Change Password
                cState = contexts_1.CognitoLoginState.RequiresNewPassword;
            }
            else if (cu.challengeName === 'MFA_SETUP') {
                // TODO: SetUp MFA
                cState = contexts_1.CognitoLoginState.SetupMFA;
            }
            else {
                // Success???
                cognitoUserSet(cu);
                cState = contexts_1.CognitoLoginState.Success;
                handleSignInSuccess(cu);
            }
        }
        catch (e) {
            if (e.code === 'PasswordResetRequiredException') {
                cState = contexts_1.CognitoLoginState.RequiresPasswordReset;
            }
            else if (e.code === 'UserNotConfirmedException') {
                const { hotel } = extraClientMetadataParams || {};
                const additionalMetadata = getExtraClientMetadata({ hotel });
                yield Auth.resendSignUp(username, Object.assign(Object.assign({}, clientMetadata), (additionalMetadata || {})));
                cState = contexts_1.CognitoLoginState.RequiresConfirmation;
            }
            else {
                throw e;
            }
        }
        if (cState) {
            cognitoLoginStateSet(cState);
            return cState;
        }
    });
    const register = (email, password, additionalAttributes, extraClientMetadataParams) => __awaiter(void 0, void 0, void 0, function* () {
        const Auth = yield (0, AsyncAuth_1.getAuth)();
        let cState;
        try {
            const { hotel } = extraClientMetadataParams || {};
            const additionalMetadata = getExtraClientMetadata({ hotel });
            const signUpResult = yield Auth.signUp({
                username: transformUsername(email),
                password,
                attributes: Object.assign({ email }, additionalAttributes),
                clientMetadata: Object.assign(Object.assign({}, clientMetadata), (additionalMetadata || {})),
            });
            if (!signUpResult.userConfirmed) {
                cState = contexts_1.CognitoLoginState.RequiresConfirmation;
            }
            else {
                cognitoUserSet(signUpResult.user);
                cState = contexts_1.CognitoLoginState.Success;
                handleSignUpSuccess();
            }
        }
        catch (e) {
            switch (e.code) {
                case 'UsernameExistsException': {
                    cState = contexts_1.CognitoLoginState.UsernameExists;
                    break;
                }
                default: {
                    throw e;
                }
            }
        }
        if (cState) {
            cognitoLoginStateSet(cState);
            return cState;
        }
    });
    const confirmAccount = (code, extraClientMetadataParams) => __awaiter(void 0, void 0, void 0, function* () {
        if (!(pendingCredentials === null || pendingCredentials === void 0 ? void 0 : pendingCredentials.email)) {
            throw new Error('No pending credentials');
        }
        const Auth = yield (0, AsyncAuth_1.getAuth)();
        const username = transformUsername(pendingCredentials.email);
        const { hotel } = extraClientMetadataParams || {};
        const additionalMetadata = getExtraClientMetadata({ hotel });
        yield Auth.confirmSignUp(username, code, {
            clientMetadata: additionalMetadata,
        });
        let cState;
        if (!(pendingCredentials === null || pendingCredentials === void 0 ? void 0 : pendingCredentials.password)) {
            cognitoLoginStateSet(contexts_1.CognitoLoginState.Login);
            cState = contexts_1.CognitoLoginState.Login;
        }
        else {
            try {
                cState = yield login(pendingCredentials.email, pendingCredentials.password, { hotel });
                if (cState === contexts_1.CognitoLoginState.Success) {
                    handleSignUpSuccess();
                }
            }
            catch (e) {
                if (e.code === 'NotAuthorizedException') {
                    cognitoLoginStateSet(contexts_1.CognitoLoginState.Login);
                    cState = contexts_1.CognitoLoginState.Login;
                }
                else {
                    throw e;
                }
            }
        }
        return cState;
    });
    const forgotPassword = (email, extraClientMetadataParams) => __awaiter(void 0, void 0, void 0, function* () {
        const Auth = yield (0, AsyncAuth_1.getAuth)();
        const username = transformUsername(email);
        const { hotel } = extraClientMetadataParams || {};
        const additionalMetadata = getExtraClientMetadata({ hotel });
        try {
            yield Auth.forgotPassword(username, Object.assign(Object.assign({}, clientMetadata), (additionalMetadata || {})));
        }
        catch (e) {
            switch (e.code) {
                case 'UserNotFoundException':
                    cognitoLoginStateSet(contexts_1.CognitoLoginState.UserNotFound);
                    return contexts_1.CognitoLoginState.UserNotFound;
                case 'InvalidParameterException':
                    cognitoLoginStateSet(contexts_1.CognitoLoginState.RequiresConfirmation);
                    yield Auth.resendSignUp(username, Object.assign(Object.assign({}, clientMetadata), (additionalMetadata || {})));
                    return contexts_1.CognitoLoginState.RequiresConfirmation;
                default:
                    throw e;
            }
        }
        cognitoLoginStateSet(contexts_1.CognitoLoginState.ForgotPassword);
        return contexts_1.CognitoLoginState.ForgotPassword;
    });
    const resetPassword = (email, code, password, extraClientMetadataParams) => __awaiter(void 0, void 0, void 0, function* () {
        const Auth = yield (0, AsyncAuth_1.getAuth)();
        const username = transformUsername(email);
        try {
            const { hotel } = extraClientMetadataParams || {};
            const additionalMetadata = getExtraClientMetadata({ hotel });
            yield Auth.forgotPasswordSubmit(username, code, password, additionalMetadata);
            yield login(email, password);
        }
        catch (e) {
            switch (e.code) {
                case 'CodeMismatchException':
                    cognitoLoginStateSet(contexts_1.CognitoLoginState.CodeMismatch);
                    return contexts_1.CognitoLoginState.CodeMismatch;
                default:
                    throw e;
            }
        }
        cognitoLoginStateSet(contexts_1.CognitoLoginState.Success);
        return contexts_1.CognitoLoginState.Success;
    });
    const changePassword = (currentPassword, newPassword, extraClientMetadataParams) => __awaiter(void 0, void 0, void 0, function* () {
        try {
            const Auth = yield (0, AsyncAuth_1.getAuth)();
            const { hotel } = extraClientMetadataParams || {};
            const additionalMetadata = getExtraClientMetadata({ hotel });
            yield Auth.changePassword(cognitoUser, currentPassword, newPassword, additionalMetadata);
        }
        catch (e) {
            switch (e.code) {
                case contexts_1.CognitoLoginState.LimitExceededException:
                    return contexts_1.CognitoLoginState.LimitExceededException;
                case contexts_1.CognitoLoginState.NotAuthorizedException:
                    return contexts_1.CognitoLoginState.NotAuthorizedException;
                default:
                    throw e;
            }
        }
        return contexts_1.CognitoLoginState.Success;
    });
    const logout = () => __awaiter(void 0, void 0, void 0, function* () {
        const Auth = yield (0, AsyncAuth_1.getAuth)();
        yield Auth.signOut();
        cognitoUserSet(null);
        cognitoLoginStateSet(contexts_1.CognitoLoginState.Login);
        DataLayer_1.default.instance.setUserId(undefined);
        return contexts_1.CognitoLoginState.Login;
    });
    const reset = () => {
        errorSet(undefined);
    };
    const getExtraClientMetadata = ({ hotel }) => {
        var _a;
        const { colors: hotelColors = colors, email, logo } = hotel || {};
        return {
            companyId: (companyId === null || companyId === void 0 ? void 0 : companyId.toString()) || '',
            companyShort,
            hotelName: (hotel === null || hotel === void 0 ? void 0 : hotel.name) || companyName || '',
            hotelUrl: (hotel === null || hotel === void 0 ? void 0 : hotel.hotelUrl) || logoLink || '',
            colorEmailHeaderBackground: ((_a = hotelColors.emails) === null || _a === void 0 ? void 0 : _a.headerBackground) || hotelColors.accent,
            colorAccent: hotelColors.accent,
            colorAccent2: hotelColors.accent2,
            colorAccent3: hotelColors.accent3,
            hotelLogo: typeof logo === 'string' ? logo : companyLogo || '',
            hotelEmail: email || '',
            hotelPhone: (hotel === null || hotel === void 0 ? void 0 : hotel.phone) || '',
            sourceUrlStr: window.location.href,
            instagramUrl: (hotel === null || hotel === void 0 ? void 0 : hotel.instagramUrl) || '',
            facebookUrl: (hotel === null || hotel === void 0 ? void 0 : hotel.facebookUrl) || '',
            linkedinUrl: (hotel === null || hotel === void 0 ? void 0 : hotel.linkedinUrl) || '',
            hotelFrontendID: selectedHotelID || '',
        };
    };
    return {
        loading,
        cognitoUser: cognitoUser,
        cognitoLoginState,
        error,
        login: (...args) => asyncWrapper(login, ...args),
        register: (...args) => asyncWrapper(register, ...args),
        confirmAccount: (...args) => asyncWrapper(confirmAccount, ...args),
        forgotPassword: (...args) => asyncWrapper(forgotPassword, ...args),
        resetPassword: (...args) => asyncWrapper(resetPassword, ...args),
        changePassword: (...args) => asyncWrapper(changePassword, ...args),
        logout: (...args) => asyncWrapper(logout, ...args),
        reset,
        pendingCredentials,
        setPendingCredentials,
    };
};
const AuthenticationContextProvider = ({ children }) => {
    const value = useAuthenticationContext();
    return (react_1.default.createElement(contexts_1.AuthenticationContext.Provider, { value: value },
        react_1.default.createElement(contexts_1.RoomstayMemberContextProvider, null, children)));
};
exports.AuthenticationContextProvider = AuthenticationContextProvider;
//# sourceMappingURL=AuthenticationContextProvider.js.map