package org.exoplatform.cs.service.migration;

import java.util.*;
import java.util.concurrent.*;

import javax.servlet.ServletContext;

import org.gatein.api.ApiException;
import org.gatein.api.navigation.*;
import org.gatein.api.security.Group;
import org.gatein.api.site.SiteId;
import org.jgroups.util.DefaultThreadFactory;

import org.exoplatform.commons.api.settings.SettingService;
import org.exoplatform.commons.api.settings.SettingValue;
import org.exoplatform.commons.api.settings.data.Context;
import org.exoplatform.commons.api.settings.data.Scope;
import org.exoplatform.commons.utils.CommonsUtils;
import org.exoplatform.container.*;
import org.exoplatform.container.component.RequestLifeCycle;
import org.exoplatform.cs.dto.SpaceDTO;
import org.exoplatform.cs.service.CSSpaceService;
import org.exoplatform.cs.service.util.CSUtils;
import org.exoplatform.cs.service.util.SpacePageUtils;
import org.exoplatform.portal.config.UserPortalConfigService;
import org.exoplatform.portal.config.model.*;
import org.exoplatform.portal.mop.SiteKey;
import org.exoplatform.portal.mop.storage.DescriptionStorage;
import org.exoplatform.portal.mop.navigation.NavigationContext;
import org.exoplatform.portal.mop.service.NavigationService;
import org.exoplatform.portal.mop.page.PageKey;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.resources.ResourceBundleManager;
import org.exoplatform.social.core.space.model.Space;
import org.exoplatform.social.core.space.spi.SpaceService;

public class SpaceNavigationMigration {
    private static final Log LOG = ExoLogger.getLogger(SpaceNavigationMigration.class);

    private SpaceService spaceService;
    private CSSpaceService cSSpaceService;
    private UserPortalConfigService portalConfigService;
    private NavigationService navigationService;
    private DescriptionStorage descriptionService;
    private ResourceBundleManager bundleManager;

    public static final String CS_SPACES_NAVIGATION_MIGRATION_DONE_KEY = "CS_SPACES_NAVIGATION_MIGRATION_DONE_KEY";
    public static final String CS_SPACES_NAVIGATION_MIGRATION_DONE = "CS_SPACES_NAVIGATION_MIGRATION_DONE";

    private static ExecutorService executorService = Executors.newSingleThreadExecutor(new DefaultThreadFactory("CS-SPACES-NAV-MIGRATION", false, false));;

    public SpaceNavigationMigration(SpaceService spaceService,
                                    CSSpaceService cSSpaceService,
                                    UserPortalConfigService portalConfigService,
                                    NavigationService navigationService,
                                    DescriptionStorage descriptionService,
                                    ResourceBundleManager bundleManager
                                    ) {

        this.spaceService = spaceService;
        this.cSSpaceService = cSSpaceService;
        this.portalConfigService = portalConfigService;
        this.navigationService = navigationService;
        this.descriptionService = descriptionService;
        this.bundleManager = bundleManager;

    }

    public void migrate() {
        PortalContainer.addInitTask(PortalContainer.getInstance().getPortalContext(), new RootContainer.PortalContainerPostInitTask() {
            @Override
            public void execute(ServletContext context, PortalContainer portalContainer) {
                executorService.submit(new Callable<Void>() {
                    @Override
                    public Void call() throws Exception {
                        RequestLifeCycle.begin(PortalContainer.getInstance());
                        if (isSpaceNavigationlMigrated()) {
                            try {
                                ExoContainerContext.setCurrentContainer(PortalContainer.getInstance());
                                LOG.info("=== Start migration of Spaces Navigation");
                                long startTime = System.currentTimeMillis();
                                migrateNav();
                                long endTime = System.currentTimeMillis();
                                LOG.info("=== Migration of Spaces Navigation is done in " + (endTime - startTime) + " ms");
                            } catch (Exception e) {
                                LOG.error("Error while migrating Spaces Navigation - Cause : " + e.getMessage(), e);
                            }
                        } else {
                            LOG.info("No Spaces Navigation to migrate");
                        }
                        return null;
                    }
                });
            }
        });
    }

    private void migrateNav(){

        int i =1;
        List<SpaceDTO> csspaces = cSSpaceService.getAllSpaces();

        for (SpaceDTO cspace : csspaces){
            Space space = spaceService.getSpaceByGroupId(cspace.getGroupId());
            if(space!=null) {

                LOG.info(i+"/"+ csspaces.size()+"----- Start update nav of Space "+space.getDisplayName());
                try {
                    portalConfigService.createUserPortalConfig(PortalConfig.GROUP_TYPE, space.getGroupId(), "CSSpace");
                    SiteKey siteKey = SiteKey.group(space.getGroupId());
                    Navigation navigation = getNav(space, siteKey);
                    Node home = navigation.getNode(NodePath.path(space.getUrl()), Nodes.visitAll());
                    Node tempHome = navigation.getNode(NodePath.path("cs"), Nodes.visitAll());
                    Iterator<Node> nodeIterator = tempHome.iterator();
                    while (nodeIterator.hasNext()) {
                        Node childNode = (Node) nodeIterator.next();
                        if(home.hasChild(childNode.getName())){
                            if (childNode.getName().equals("guide")) break;
                            home.removeChild(childNode.getName());
                        }
                        Node newNode = home.addChild(childNode.getName());
                        newNode.setIconName(childNode.getIconName());
                        newNode.setPageId(childNode.getPageId());
                        newNode.setVisibility(childNode.getVisibility());
                        navigation.saveNode(home);
                        navigation.removeNode(childNode.getNodePath());

                        // Convert Portlet preferences
                        convertChildApplicationPreferences(space, siteKey, newNode);
                    }
                    // Set home page
                    home.setPageId(tempHome.getPageId());
                    navigation.saveNode(home);
                    // Convert Portlet preferences

                    convertChildApplicationPreferences(space, siteKey, home);

                    // Remove temporary home navigation page
                    navigation.removeNode(tempHome.getNodePath());

                } catch (Exception e) {
                    LOG.error("NewPortalConfig error: " + e.getMessage(), e);
                }
            }

            LOG.info("----- Space "+space.getDisplayName()+" Nav updated");


            i++;
        }
        setSpaceNavigationMigrationDone();
    }

    private Navigation getNav(Space space, SiteKey siteKey){
        try {
            NavigationContext ctx = navigationService.loadNavigation(siteKey);
            if (ctx == null) return null;
            Group group = new Group(space.getGroupId());
            return new NavigationImpl(new SiteId(group), navigationService, ctx, descriptionService, bundleManager);
        } catch (Throwable t) {
            throw new ApiException("Failed to load navigation", t);
        }

    }


    private void convertChildApplicationPreferences(Space space, SiteKey siteKey, Node newNode) throws Exception {
        PageKey pageKey = new PageKey(siteKey, newNode.getPageId().getPageName());
        Page newPage = CSUtils.getPage(portalConfigService.getDataStorage(), portalConfigService.getPageService(), pageKey);
        ArrayList<ModelObject> modelObjects = newPage.getChildren();
        SpacePageUtils.convert(modelObjects, space.getGroupId());
    }

    public static void setSpaceNavigationMigrationDone() {
        CommonsUtils.getService(SettingService.class).set(Context.GLOBAL, Scope.APPLICATION.id(CS_SPACES_NAVIGATION_MIGRATION_DONE_KEY), CS_SPACES_NAVIGATION_MIGRATION_DONE, SettingValue.create("true"));
    }


    public static boolean isSpaceNavigationlMigrated() {
        SettingValue<?> setting = CommonsUtils.getService(SettingService.class).get(Context.GLOBAL, Scope.APPLICATION.id(CS_SPACES_NAVIGATION_MIGRATION_DONE_KEY), CS_SPACES_NAVIGATION_MIGRATION_DONE);
        return (setting != null && setting.getValue().equals("true"));
    }

}
