/*
 * Copyright (C) 2021 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.services.attachments.utils;

import org.exoplatform.commons.exception.ObjectNotFoundException;
import org.exoplatform.services.attachments.model.Attachment;
import org.exoplatform.services.attachments.model.Permission;
import org.exoplatform.services.attachments.rest.model.AttachmentEntity;
import org.exoplatform.services.cms.documents.DocumentService;
import org.exoplatform.services.cms.link.LinkManager;
import org.exoplatform.services.cms.mimetype.DMSMimeTypeResolver;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.core.ExtendedSession;
import org.exoplatform.services.jcr.impl.core.NodeImpl;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.exoplatform.social.core.identity.model.Identity;
import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.social.rest.entity.IdentityEntity;

import java.util.LinkedHashMap;

import javax.jcr.*;

public class EntityBuilder {

  private static final Log    LOG                   = ExoLogger.getLogger(EntityBuilder.class);

  private static final String IDENTITIES_REST_PATH  = "/v1/social/identities";                 // NOSONAR

  private static final String IDENTITIES_EXPAND     = "all";

  public static final AttachmentEntity fromAttachment(IdentityManager identityManager, Attachment attachment) {
    return new AttachmentEntity(attachment.getId(),
                                attachment.getTitle(),
                                attachment.getSize(),
                                attachment.getMimetype(),
                                attachment.getPath(),
                                attachment.getIsPublic(),
                                attachment.getAcl(),
                                null,
                                attachment.getCreated(),
                                getIdentityEntity(identityManager, attachment.getUpdater()),
                                attachment.getUpdated(),
                                attachment.getDownloadUrl(),
                                attachment.getOpenUrl(),
                                attachment.getPreviewBreadcrumb(),
                                attachment.getVersion(),
                                attachment.isCloudDrive()

    );
  }

  public static final Attachment fromAttachmentNode(RepositoryService repositoryService,
                                                    DocumentService documentService,
                                                    LinkManager linkManager,
                                                    String workspace,
                                                    Session session,
                                                    String attachmentId) throws Exception {
    Node attachmentNode = null;
    Permission acl = new Permission();
    ExtendedSession extendedSession = (ExtendedSession) session;
    try {
      attachmentNode = extendedSession.getNodeByIdentifier(attachmentId);
    } catch (AccessDeniedException e) {
      Attachment privateAttachment = new Attachment();
      acl.setCanAccess(false);
      privateAttachment.setId(attachmentId);
      privateAttachment.setAcl(acl);
      return privateAttachment;
    } catch (ItemNotFoundException e) {
      throw new ObjectNotFoundException("Node with id " + attachmentId + " wasn't found");
    }
    Node originalAttachmentNode = attachmentNode;
    if (linkManager.isLink(attachmentNode)) {
      originalAttachmentNode = linkManager.getTarget(attachmentNode);
      if (originalAttachmentNode == null) {
        throw new ObjectNotFoundException("Target Node with of symlink " + attachmentId + " wasn't found");
      }
    }

    Attachment attachment = new Attachment();
    attachment.setId(((NodeImpl) attachmentNode).getIdentifier());
    String attachmentsTitle = getStringProperty(originalAttachmentNode, "exo:title");
    attachment.setTitle(attachmentsTitle);
    String attachmentsPath = attachmentNode.getPath();
    attachment.setPath(attachmentsPath);
    attachment.setCreated(getStringProperty(originalAttachmentNode, "exo:dateCreated"));
    if (originalAttachmentNode.hasProperty("exo:dateModified")) {
      attachment.setUpdated(getStringProperty(originalAttachmentNode, "exo:dateModified"));
    } else {
      attachment.setUpdated(null);
    }
    if (originalAttachmentNode.hasProperty("exo:lastModifier")) {
      attachment.setUpdater(getStringProperty(originalAttachmentNode, "exo:lastModifier"));
    } else {
      attachment.setUpdater(null);
    }
    attachment.setCloudDrive(originalAttachmentNode.hasProperty("ecd:driveUUID"));
    DMSMimeTypeResolver mimeTypeResolver = DMSMimeTypeResolver.getInstance();
    String mimetype = mimeTypeResolver.getMimeType(attachmentsTitle);
    attachment.setMimetype(mimetype);

    long size = originalAttachmentNode.getNode("jcr:content").getProperty("jcr:data").getLength();
    attachment.setSize(size);

    String downloadUrl = getDownloadUrl(repositoryService, workspace, attachmentsPath);
    attachment.setDownloadUrl(downloadUrl);

    String openUrl = getUrl(documentService, attachmentId);
    attachment.setOpenUrl(openUrl);

    String attachmentsVersion = getStringProperty(originalAttachmentNode, "exo:baseVersion");
    attachment.setVersion(attachmentsVersion);

    LinkedHashMap<String, String> previewBreadcrumb = new LinkedHashMap<>();
    try {
      previewBreadcrumb = documentService.getFilePreviewBreadCrumb(attachmentNode);
    } catch (Exception e) {
      LOG.error("Error while getting file preview breadcrumb " + ((NodeImpl) originalAttachmentNode).getIdentifier(), e);
    }
    attachment.setPreviewBreadcrumb(previewBreadcrumb);

    acl.setCanAccess(true);
    attachment.setAcl(acl);

    return attachment;
  }

  private static IdentityEntity getIdentityEntity(IdentityManager identityManager, String ownerId) {
    Identity identity = getIdentity(identityManager, ownerId);
    if (identity == null) {
      return null;
    }
    return org.exoplatform.social.rest.api.EntityBuilder.buildEntityIdentity(identity, IDENTITIES_REST_PATH, IDENTITIES_EXPAND);
  }

  private static final Identity getIdentity(IdentityManager identityManager, String identityId) {
    return identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, identityId);
  }

  private static String getStringProperty(Node node, String propertyName) throws RepositoryException {
    if (node.hasProperty(propertyName)) {
      return node.getProperty(propertyName).getString();
    }
    return "";
  }

  private static String getDownloadUrl(RepositoryService repositoryService, String workspace, String nodePath) {
    String restContextName = WCMCoreUtils.getRestContextName();

    String repositoryName = getRepositoryName(repositoryService);

    StringBuffer downloadUrl = new StringBuffer();
    downloadUrl.append('/')
               .append(restContextName)
               .append("/jcr/")
               .append(repositoryName)
               .append('/')
               .append(workspace)
               .append(nodePath);
    return downloadUrl.toString();
  }

  /**
   * Provides the URL of the document in its Drive
   * @param documentService
   * @param nodeIdentifier
   * @return URL of type String
   */
  private static String getUrl(DocumentService documentService, String nodeIdentifier) {
    String url = "";
    try {
      url = documentService.getLinkInDocumentsAppByIdentifier(nodeIdentifier);
    } catch (Exception e) {
      LOG.error("Cannot get url of document " + nodeIdentifier, e);
    }
    return url;
  }

  private static String getRepositoryName(RepositoryService repositoryService) {
    try {
      return repositoryService.getCurrentRepository().getConfiguration().getName();
    } catch (RepositoryException e) {
      LOG.debug("Cannot get repository name", e);
      return "repository";
    }
  }
}
