/*
 * JBoss, a division of Red Hat
 * Copyright 2013, Red Hat Middleware, LLC, and individual
 * contributors as indicated by the @authors tag. See the
 * copyright.txt in the distribution for a full listing of
 * individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.exoplatform.portal.account;

import java.util.Collection;

import javax.servlet.http.HttpSession;

import org.exoplatform.portal.application.PortalRequestContext;
import org.exoplatform.portal.application.UserProfileLifecycle;
import org.exoplatform.portal.webui.util.Util;
import org.exoplatform.portal.webui.workspace.UIWorkingWorkspace;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.organization.User;
import org.exoplatform.services.organization.UserProfile;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.web.CacheUserProfileFilter;
import org.exoplatform.web.application.ApplicationMessage;
import org.exoplatform.web.application.JavascriptManager;
import org.exoplatform.web.application.Parameter;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.config.annotation.EventConfig;
import org.exoplatform.webui.core.UIApplication;
import org.exoplatform.webui.core.lifecycle.Lifecycle;
import org.exoplatform.webui.event.Event;
import org.exoplatform.webui.event.EventListener;
import org.exoplatform.webui.form.UIForm;
import org.exoplatform.webui.form.UIFormStringInput;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.gatein.security.oauth.spi.AccessTokenContext;
import org.gatein.security.oauth.spi.OAuthProviderType;
import org.gatein.security.oauth.common.OAuthConstants;
import org.gatein.security.oauth.spi.OAuthProviderTypeRegistry;
import org.gatein.security.oauth.spi.SocialNetworkService;
import org.gatein.security.oauth.exception.OAuthException;
import org.gatein.security.oauth.exception.OAuthExceptionCode;

/**
 * Social networks tab of user profile
 *
 * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
 */
@ComponentConfig(lifecycle = Lifecycle.class, template = "system:/groovy/portal/webui/portal/UIAccountSocial.gtmpl",
                 events    = { @EventConfig(listeners = UIAccountSocial.UnlinkSocialAccountActionListener.class) })
public class UIAccountSocial extends UIForm {

    private static final Logger log = LoggerFactory.getLogger(UIAccountSocial.class);

    private static final String PARAM_PROVIDER_FOR_UNLINK = "_paramProviderForUnlink";

    public UIAccountSocial() throws Exception {
        for (OAuthProviderType oauthPrType : getOAuthProviderTypes()) {
            UIFormStringInput uiInput = new UIFormStringInput(oauthPrType.getUserNameAttrName(), null);
            uiInput.setReadOnly(true);
            addUIFormInput(uiInput);
        }

        updateUIFields();
    }

    public Collection<OAuthProviderType> getOAuthProviderTypes() {
        // For now return all available oauthProviderTypes
        return getApplicationComponent(OAuthProviderTypeRegistry.class).getEnabledOAuthProviders();
    }

    @Override
    public void processRender(WebuiRequestContext context) throws Exception {
        updateUIFields();
        super.processRender(context);
    }

    void requestActive() {
        UIAccountSetting uiAccountSetting = this.getAncestorOfType(UIAccountSetting.class);
        if (uiAccountSetting != null) {
            uiAccountSetting.setActiveChildId(this.getId());
        }
    }

    private void updateUIFields() {
        UserProfile userProfile = (UserProfile)Util.getPortalRequestContext().getAttribute(UserProfileLifecycle.USER_PROFILE_ATTRIBUTE_NAME);

        if (userProfile == null) {
            return;
        }

        for (OAuthProviderType oauthPrType : getOAuthProviderTypes()) {
            String usernameAttrName = oauthPrType.getUserNameAttrName();
            String oauthUsername = userProfile.getAttribute(usernameAttrName);
            getUIStringInput(usernameAttrName).setValue(oauthUsername);
        }
    }

    public void saveURLAfterLinkSocialAccount() {
        PortalRequestContext prContext = Util.getPortalRequestContext();
        HttpSession session = prContext.getRequest().getSession();
        session.setAttribute(OAuthConstants.ATTRIBUTE_URL_TO_REDIRECT_AFTER_LINK_SOCIAL_ACCOUNT, prContext.getRequestURI());
    }

    public String getLinkSocialAccountURL(OAuthProviderType oauthPrType) {
        PortalRequestContext prc = Util.getPortalRequestContext();
        String requestURI = new StringBuilder(prc.getRequestURI())
                                .append("?").append(UIAccountSetting.PARAM_ACTIVE_CHILD_ID)
                                .append("=").append(this.getId())
                                .toString();
        return oauthPrType.getInitOAuthURL(prc.getRequestContextPath(), requestURI);
    }

    public String getUnlinkSocialAccountURL(OAuthProviderType oauthPrType) throws Exception {
        Parameter[] params = new Parameter[] { new Parameter(PARAM_PROVIDER_FOR_UNLINK, oauthPrType.getKey()) };
        return super.event("UnlinkSocialAccount", null, params);
    }

    public static class UnlinkSocialAccountActionListener extends EventListener<UIAccountSocial> {

        public void execute(Event<UIAccountSocial> event) throws Exception {
            UIAccountSocial uiForm = event.getSource();

            OrganizationService service = uiForm.getApplicationComponent(OrganizationService.class);
            WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
            PortalRequestContext prContext = Util.getPortalRequestContext();
            UIApplication uiApp = context.getUIApplication();

            uiForm.requestActive();

            ConversationState state = ConversationState.getCurrent();
            String userName = ((User) state.getAttribute(CacheUserProfileFilter.USER_PROFILE)).getUserName();
            User user = service.getUserHandler().findUserByName(userName);

            if (user != null) {
                UserProfile userProfile = (UserProfile)prContext.getAttribute(UserProfileLifecycle.USER_PROFILE_ATTRIBUTE_NAME);

                String unlinkProviderKey = prContext.getRequestParameter(PARAM_PROVIDER_FOR_UNLINK);
                OAuthProviderType<AccessTokenContext> oauthProviderTypeToUnlink = uiForm.getApplicationComponent(OAuthProviderTypeRegistry.class).getOAuthProvider(unlinkProviderKey, AccessTokenContext.class);

                // Obtain current accessToken
                AccessTokenContext accessToken = uiForm.getApplicationComponent(SocialNetworkService.class).getOAuthAccessToken(oauthProviderTypeToUnlink, userName);

                // Unlink social account in userProfile (AccessTokenInvalidationListener will automatically remove accessToken)
                if (oauthProviderTypeToUnlink != null) {
                    userProfile.setAttribute(oauthProviderTypeToUnlink.getUserNameAttrName(), null);
                } else {
                    log.warn("Social account field to unlink not found");
                }
                service.getUserProfileHandler().saveUserProfile(userProfile, true);

                // Revoke accessToken remotely
                if (accessToken != null) {
                    try {
                        oauthProviderTypeToUnlink.getOauthProviderProcessor().revokeToken(accessToken);
                    } catch (OAuthException oe) {
                        if (OAuthExceptionCode.TOKEN_REVOCATION_FAILED.equals(oe.getExceptionCode())) {
                            Throwable t = oe.getCause() != null ? oe.getCause() : oe;
                            ApplicationMessage appMessage = new ApplicationMessage("UIAccountSocial.msg.failed-revoke", null, ApplicationMessage.WARNING);
                            appMessage.setArgsLocalized(false);
                            uiApp.addMessage(appMessage);
                            log.warn("Revocation of accessToken failed for user " + userName + ". Details: " + t.getClass() + ": " + t.getMessage());
                        } else {
                            throw oe;
                        }
                    }
                }

                Object[] args = { oauthProviderTypeToUnlink.getFriendlyName(), userName};
                ApplicationMessage appMessage = new ApplicationMessage("UIAccountSocial.msg.successful-unlink", args);
                appMessage.setArgsLocalized(false);
                uiApp.addMessage(appMessage);

                prContext.setAttribute(UserProfileLifecycle.USER_PROFILE_ATTRIBUTE_NAME, userProfile);
                uiForm.updateUIFields();
                prContext.addUIComponentToUpdateByAjax(uiForm);

                UIWorkingWorkspace uiWorkingWS = Util.getUIPortalApplication().getChild(UIWorkingWorkspace.class);
                uiWorkingWS.updatePortletsByName("UserInfoPortlet");
                uiWorkingWS.updatePortletsByName("OrganizationPortlet");
            } else {
                JavascriptManager jsManager = Util.getPortalRequestContext().getJavascriptManager();
                jsManager.require("SHARED/base").addScripts(
                        "if(confirm('"
                                + Util.getPortalRequestContext().getApplicationResourceBundle()
                                .getString("UIAccountProfiles.msg.NotExistingAccount") + "')) {eXo.portal.logout();}");
            }
        }
    }

}
