package exo.exoplatform.services.wcm.extensions.publication;

import java.util.Date;
import java.util.LinkedList;

import javax.jcr.Node;

import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.component.RequestLifeCycle;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.config.model.Page;
import org.exoplatform.portal.config.model.PortalConfig;
import org.exoplatform.portal.mop.EventType;
import org.exoplatform.portal.mop.navigation.NavigationService;
import org.exoplatform.portal.pom.config.POMSessionManager;
import org.exoplatform.portal.pom.spi.portlet.Portlet;
import org.exoplatform.services.ecm.publication.PublicationPlugin;
import org.exoplatform.services.listener.Event;
import org.exoplatform.services.listener.Listener;
import org.exoplatform.services.listener.ListenerService;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.security.Identity;
import org.exoplatform.services.wcm.core.NodetypeConstant;
import org.exoplatform.services.wcm.publication.PublicationDefaultStates;
import org.exoplatform.services.wcm.publication.WCMPublicationService;
import org.exoplatform.services.wcm.publication.WebpagePublicationPlugin;
import org.exoplatform.services.wcm.extensions.publication.lifecycle.authoring.AuthoringPublicationPlugin;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;

public class TestWCMPublicationService extends BasePublicationTestCase {

  private static final String CURRENT_STATE = "publication:currentState";
  private static final String TEST = "test";
  private static final String ENROLLED = "enrolled";
  private static final String PUBLISHED = "published";

  private WCMPublicationService publicationService_;
  private WebpagePublicationPlugin plugin_;
  private Node node_;
  private Node testSite;
  private Node documentTest;
  // -----------------
  /** . */
  private final String testPage = "portal::classic::testPage";

  /** . */
  private final String testPortletPreferences = "portal#classic:/web/BannerPortlet/testPortletPreferences";

  /** . */
  private DataStorage storage_;

  /** . */
  private NavigationService navService;

  /** . */
  private POMSessionManager mgr;

  /** . */
  private LinkedList<Event> events;

  /** . */
  private ListenerService listenerService;

  /** . */
  private OrganizationService org;

  public void setUp() throws Exception {
    super.setUp();
    publicationService_ = WCMCoreUtils.getService(WCMPublicationService.class);
    RequestLifeCycle.begin(PortalContainer.getInstance());
    applySystemSession();
    Node rootSite = (Node) session.getItem("/sites content/live");
    testSite = rootSite.addNode(TEST);
    documentTest = testSite.addNode("documents");
    node_ = testSite.addNode(TEST);
    node_.addMixin(NodetypeConstant.MIX_REFERENCEABLE);
    session.save();
    ConversationState c = new ConversationState(new Identity(session.getUserID()));
    ConversationState.setCurrent(c);
    plugin_ = new AuthoringPublicationPlugin();
    plugin_.setName("Authoring publication");
    plugin_.setDescription("Authoring publication");
    publicationService_.addPublicationPlugin(plugin_);

    // --------------------------------
    Listener listener = new Listener() {
      @Override
      public void onEvent(Event event) throws Exception {
        events.add(event);
      }
    };

    PortalContainer container = PortalContainer.getInstance();
    storage_ = (DataStorage) container.getComponentInstanceOfType(DataStorage.class);
    mgr = (POMSessionManager) container.getComponentInstanceOfType(POMSessionManager.class);
    navService = (NavigationService) container.getComponentInstanceOfType(NavigationService.class);
    events = new LinkedList<Event>();
    listenerService = (ListenerService) container.getComponentInstanceOfType(ListenerService.class);
    org = WCMCoreUtils.getService(OrganizationService.class);

    listenerService.addListener(DataStorage.PAGE_CREATED, listener);
    listenerService.addListener(DataStorage.PAGE_REMOVED, listener);
    listenerService.addListener(DataStorage.PAGE_UPDATED, listener);
    listenerService.addListener(EventType.NAVIGATION_CREATED, listener);
    listenerService.addListener(EventType.NAVIGATION_DESTROYED, listener);
    listenerService.addListener(EventType.NAVIGATION_UPDATED, listener);
    listenerService.addListener(DataStorage.PORTAL_CONFIG_CREATED, listener);
    listenerService.addListener(DataStorage.PORTAL_CONFIG_UPDATED, listener);
    listenerService.addListener(DataStorage.PORTAL_CONFIG_REMOVED, listener);
  }

  public void tearDown() throws Exception {
    publicationService_.getWebpagePublicationPlugins().clear();
    node_.remove();
    testSite.remove();
    session.save();
    RequestLifeCycle.end();
    super.tearDown();
  }

  /**
   * Test if a node is enrolled into a lifecycle
   * 
   * @result current state of enrolled node is "enrolled"
   */
  public void testEnrollNodeInLifecycle1() throws Exception {
    publicationService_.enrollNodeInLifecycle(node_, plugin_.getLifecycleName());
    assertEquals(ENROLLED, node_.getProperty(CURRENT_STATE).getString());
  }

  /**
   * Test if a node in site is enrolled into a lifecycle
   * 
   * @result current state of enrolled is node "draft"
   */
  public void testEnrollNodeInLifecycle2() throws Exception {
    publicationService_.enrollNodeInLifecycle(node_, "test", node_.getSession().getUserID());
    assertEquals(PublicationDefaultStates.DRAFT, node_.getProperty(CURRENT_STATE).getString());
  }

  /**
   * Test if a node is enrolled into WCM Lifecyle
   * 
   * @result node is enrolled into WCM Lifecycle
   */
  public void testIsEnrolledWCMInLifecycle() throws Exception {
    assertFalse(publicationService_.isEnrolledInWCMLifecycle(node_));
    publicationService_.enrollNodeInLifecycle(node_, plugin_.getLifecycleName());
    assertTrue(publicationService_.isEnrolledInWCMLifecycle(node_));
  }

  /**
   * Test the getContentState function
   * 
   * @result getContentState returns the "enrolled" status when a node is
   *         enrolled into a lifecycle.
   */
  public void testGetContentState() throws Exception {
    publicationService_.enrollNodeInLifecycle(node_, plugin_.getLifecycleName());
    assertEquals(ENROLLED, publicationService_.getContentState(node_));
  }

  /**
   * Test the unsubcribeLifecycle function
   * 
   * @result Node is no longer enrolled into any lifecycle
   */
  public void testUnsubscribeLifecycle() throws Exception {
    publicationService_.enrollNodeInLifecycle(node_, plugin_.getLifecycleName());
    publicationService_.unsubcribeLifecycle(node_);
    assertFalse(publicationService_.isEnrolledInWCMLifecycle(node_));
  }

  /**
   * Test if the state of a node can be changed in a lifecycle
   * 
   * @result state of node can be changed
   */
  public void testUpdateLifecyleOnChangeContent1() throws Exception {
    publicationService_.updateLifecyleOnChangeContent(node_, "test", node_.getSession().getUserID(),
        PublicationDefaultStates.PUBLISHED);

    assertEquals(PublicationDefaultStates.PUBLISHED, publicationService_.getContentState(node_));

    publicationService_.updateLifecyleOnChangeContent(node_, "test", node_.getSession().getUserID(),
        PublicationDefaultStates.DRAFT);
    assertEquals(PublicationDefaultStates.DRAFT, publicationService_.getContentState(node_));
  }

  /**
   * Test if the state of a node can be changed into default state
   * 
   * @result state of node is in "draft"
   */

  public void testUpdateLifecyleOnChangeContent2() throws Exception {

    publicationService_.updateLifecyleOnChangeContent(node_, "test", node_.getSession().getUserID());
    assertEquals(PublicationDefaultStates.DRAFT, publicationService_.getContentState(node_));
  }

  /**
   * Test if child node of webcontent cannot be enrolled into a lifecycle
   * 
   * @result child node of webcontent cannot be enrolled into a lifecyle
   */

  public void testUpdateLifecyleOnChangeContent3() throws Exception {
    String htmlData = "This is the default.html file.";
    Node webContent = createWebcontentNode(documentTest, "webcontent", "html", "css", "js");
    Node documentFolder = webContent.getNode("documents");

    Node htmlNode;
    try {
      htmlNode = documentFolder.getNode("default.html");
    } catch (Exception ex) {
      htmlNode = documentFolder.addNode("default.html", "nt:file");
    }
    if (!htmlNode.isNodeType("exo:htmlFile"))
      htmlNode.addMixin("exo:htmlFile");
    Node htmlContent;
    try {
      htmlContent = htmlNode.getNode("jcr:content");
    } catch (Exception ex) {
      htmlContent = htmlNode.addNode("jcr:content", "nt:resource");
    }
    htmlContent.setProperty("jcr:encoding", "UTF-8");
    htmlContent.setProperty("jcr:mimeType", "text/html");
    htmlContent.setProperty("jcr:lastModified", new Date().getTime());
    htmlContent.setProperty("jcr:data", htmlData);
    documentFolder.save();
    publicationService_.updateLifecyleOnChangeContent(webContent, "test", webContent.getSession().getUserID());
    assertEquals(PublicationDefaultStates.DRAFT, publicationService_.getContentState(webContent));
    publicationService_.updateLifecyleOnChangeContent(htmlNode, "test", webContent.getSession().getUserID());
    // ECMS-6460: Do not allow publishing children of webcontent
    assertEquals(null, publicationService_.getContentState(htmlNode));
  }

  /**
   * Creates the webcontent node.
   * 
   * @param parentNode
   *          the parent node
   * @param nodeName
   *          the node name
   * @param htmlData
   *          the html data
   * @param cssData
   *          the css data
   * @param jsData
   *          the js data
   * 
   * @return the node
   * 
   * @throws Exception
   *           the exception
   */
  protected Node createWebcontentNode(Node parentNode, String nodeName, String htmlData, String cssData, String jsData)
      throws Exception {
    Node webcontent = parentNode.addNode(nodeName, "exo:webContent");
    webcontent.setProperty("exo:title", nodeName);
    Node htmlNode;
    try {
      htmlNode = webcontent.getNode("default.html");
    } catch (Exception ex) {
      htmlNode = webcontent.addNode("default.html", "nt:file");
    }
    if (!htmlNode.isNodeType("exo:htmlFile"))
      htmlNode.addMixin("exo:htmlFile");
    Node htmlContent;
    try {
      htmlContent = htmlNode.getNode("jcr:content");
    } catch (Exception ex) {
      htmlContent = htmlNode.addNode("jcr:content", "nt:resource");
    }
    htmlContent.setProperty("jcr:encoding", "UTF-8");
    htmlContent.setProperty("jcr:mimeType", "text/html");
    htmlContent.setProperty("jcr:lastModified", new Date().getTime());
    if (htmlData == null)
      htmlData = "This is the default.html file.";
    htmlContent.setProperty("jcr:data", htmlData);

    Node jsFolder;
    try {
      jsFolder = webcontent.getNode("js");
    } catch (Exception ex) {
      jsFolder = webcontent.addNode("js", "exo:jsFolder");
    }

    Node documentsFolder;
    try {
      documentsFolder = webcontent.getNode("documents");
    } catch (Exception ex) {
      documentsFolder = webcontent.addNode("documents", "nt:folder");
    }

    Node jsNode;
    try {
      jsNode = jsFolder.getNode("default.js");
    } catch (Exception ex) {
      jsNode = jsFolder.addNode("default.js", "nt:file");
    }
    if (!jsNode.isNodeType("exo:jsFile"))
      jsNode.addMixin("exo:jsFile");
    jsNode.setProperty("exo:active", true);
    jsNode.setProperty("exo:priority", 1);
    jsNode.setProperty("exo:sharedJS", true);

    Node jsContent;
    try {
      jsContent = jsNode.getNode("jcr:content");
    } catch (Exception ex) {
      jsContent = jsNode.addNode("jcr:content", "nt:resource");
    }
    jsContent.setProperty("jcr:encoding", "UTF-8");
    jsContent.setProperty("jcr:mimeType", "text/javascript");
    jsContent.setProperty("jcr:lastModified", new Date().getTime());
    if (jsData == null)
      jsData = "This is the default.js file.";
    jsContent.setProperty("jcr:data", jsData);

    Node cssFolder;
    try {
      cssFolder = webcontent.getNode("css");
    } catch (Exception ex) {
      cssFolder = webcontent.addNode("css", "exo:cssFolder");
    }
    Node cssNode;
    try {
      cssNode = cssFolder.getNode("default.css");
    } catch (Exception ex) {
      cssNode = cssFolder.addNode("default.css", "nt:file");
    }
    if (!cssNode.isNodeType("exo:cssFile"))
      cssNode.addMixin("exo:cssFile");
    cssNode.setProperty("exo:active", true);
    cssNode.setProperty("exo:priority", 1);
    cssNode.setProperty("exo:sharedCSS", true);

    Node cssContent;
    try {
      cssContent = cssNode.getNode("jcr:content");
    } catch (Exception ex) {
      cssContent = cssNode.addNode("jcr:content", "nt:resource");
    }
    cssContent.setProperty("jcr:encoding", "UTF-8");
    cssContent.setProperty("jcr:mimeType", "text/css");
    cssContent.setProperty("jcr:lastModified", new Date().getTime());
    if (cssData == null)
      cssData = "This is the default.css file.";
    cssContent.setProperty("jcr:data", cssData);

    Node mediaFolder;
    try {
      mediaFolder = webcontent.getNode("medias");
    } catch (Exception ex) {
      mediaFolder = webcontent.addNode("medias");
    }
    if (!mediaFolder.hasNode("images"))
      mediaFolder.addNode("images", "nt:folder");
    if (!mediaFolder.hasNode("videos"))
      mediaFolder.addNode("videos", "nt:folder");
    if (!mediaFolder.hasNode("audio"))
      mediaFolder.addNode("audio", "nt:folder");
    session.save();
    return webcontent;
  }

}
