package com.atlassian.bitbucket.rest.comment;

import com.atlassian.bitbucket.comment.Comment;
import com.atlassian.bitbucket.comment.CommentOperations;
import com.atlassian.bitbucket.comment.DiffCommentAnchor;
import com.atlassian.bitbucket.property.PropertyMap;
import com.atlassian.bitbucket.rest.annotation.JsonSurrogate;
import com.atlassian.bitbucket.rest.property.RestPropertySupport;
import com.atlassian.bitbucket.rest.task.RestTask;
import com.atlassian.bitbucket.rest.user.RestApplicationUser;
import com.atlassian.bitbucket.task.Task;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.codehaus.jackson.map.annotate.JsonSerialize;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

@JsonSerialize
@JsonSurrogate(Comment.class)
public class RestComment extends RestPropertySupport {

    public static final Function<DiffCommentAnchor, RestComment> FROM_ANCHOR = new Function<DiffCommentAnchor, RestComment>() {

        @Override
        public RestComment apply(DiffCommentAnchor anchor) {
            return new RestComment(anchor.getComment(), new RestDiffCommentAnchor(anchor), REST_TRANSFORM);
        }
    };
    public static final Function<Comment, RestComment> REST_TRANSFORM = new Function<Comment, RestComment>() {

        @Override
        public RestComment apply(Comment comment) {
            return new RestComment(comment, this);
        }
    };

    @SuppressWarnings("unused") //Required for Jersey
    public RestComment() {
    }

    public RestComment(Map<String, Object> values) {
        putAll(values);
    }

    public RestComment(@Nonnull Comment comment) {
        this(comment, REST_TRANSFORM);
    }

    public RestComment(@Nonnull Comment comment, @Nullable Function<Comment, RestComment> replyTransformer) {
        this(comment, null, replyTransformer);
    }

    public RestComment(@Nonnull Comment comment, @Nullable RestDiffCommentAnchor anchor,
            @Nullable Function<Comment, RestComment> replyTransformer) {
        this(comment, anchor, replyTransformer, RestTask.REST_TRANSFORM);
    }

    public RestComment(@Nonnull Comment comment, @Nullable RestDiffCommentAnchor anchor,
            @Nullable Function<Comment, RestComment> replyTransformer,
            @Nullable Function<Task, RestTask> taskTransformer) {
        this(comment.getId(), comment.getVersion(), comment.getText(), anchor, new RestApplicationUser(comment.getAuthor()),
                comment.getCreatedDate(), comment.getUpdatedDate(), null,
                replyTransformer == null ? null : transform(comment.getComments(), replyTransformer),
                comment.getProperties(),
                taskTransformer == null ? null : transform(comment.getTasks(), taskTransformer));

        CommentOperations operations = comment.getPermittedOperations();
        if (operations != null) {
            Map<String, Boolean> permittedOperations = Maps.newHashMap();
            permittedOperations.put("editable", operations.isEditable());
            permittedOperations.put("deletable", operations.isDeletable());

            put("permittedOperations", permittedOperations);
        }
    }

    // Required for REST documentation
    private RestComment(long id, Integer version, String text, RestDiffCommentAnchor anchor, RestApplicationUser author,
                        Date created, Date updated, RestComment parent, List<RestComment> replies,
                        Map<String, Boolean> permittedOperations, PropertyMap properties, List<RestTask> tasks) {
        this(id, version, text, anchor, author, created, updated, parent, replies, properties, tasks);

        putIfNotEmpty("permittedOperations", permittedOperations);
    }

    // Required for REST documentation
    private RestComment(long id, Integer version, String text, RestDiffCommentAnchor anchor, RestApplicationUser author,
                        Date created, Date updated, RestComment parent, List<RestComment> replies,
                        PropertyMap properties, List<RestTask> tasks) {
        super(properties);

        if (id > 0L) {
            put("id", id);
        }
        putIfNotNull("version", version);
        putIfNotNull("text", text);
        putIfNotNull("author", author);
        putIfNotNull("createdDate", created);
        putIfNotNull("updatedDate", updated);
        putIfNotNull("comments", replies);
        putIfNotNull("parent", parent);
        putIfNotNull("anchor", anchor);
        putIfNotNull("tasks", tasks);
    }

    @SuppressWarnings("unchecked")
    public RestDiffCommentAnchor getAnchor() {
        Object anchor = get("anchor");
        if (anchor instanceof RestDiffCommentAnchor) {
            return (RestDiffCommentAnchor) anchor;
        } else if (anchor instanceof Map) {
            return new RestDiffCommentAnchor((Map) anchor);
        }
        return null;
    }

    public long getId() {
        return getLongProperty("id");
    }

    @SuppressWarnings("unchecked")
    public RestComment getParent() {
        Object parent = get("parent");
        if (parent instanceof RestComment) {
            return (RestComment) parent;
        } else if (parent instanceof Map) {
            return new RestComment((Map) parent);
        }
        return null;
    }

    public String getText() {
        return getStringProperty("text");
    }

    public void setHtml(String html) {
        put("html", html);
    }

    public int getVersion() {
        return getIntProperty("version");
    }

    public boolean isAnchored() {
        return containsKey("anchor");
    }

    public boolean isReply() {
        return containsKey("parent");
    }

    // REST Documentation Examples

    public static final RestComment REQUEST_EXAMPLE_ID = new RestComment(1L, null, null, null, null, null, null, null,
            null, null, null, null);
    public static final RestComment REQUEST_EXAMPLE = new RestComment(0L, null, "An insightful comment.",
            RestDiffCommentAnchor.LINE_REQUEST_MINIMUM_EXAMPLE, null, null, null, REQUEST_EXAMPLE_ID, null, null, null,
            null);
    public static final RestComment REQUEST_EXISTING_EXAMPLE = new RestComment(0L, 0, "A pithy comment.", null, null,
            null, null, null, null, null, null, null); // nothing but the text can be updated
    public static final RestComment RESPONSE_EXAMPLE_NO_REPLY = new RestComment(1L, 1, "An insightful comment.", null,
            RestApplicationUser.RESPONSE_EXAMPLE, new Date(), new Date(), null, Lists.<RestComment>newArrayList(),
            ImmutableMap.of("editable", true, "deletable", true),
            new PropertyMap.Builder().property("key", "value").build(),
            Lists.<RestTask>newArrayList());
    public static final RestComment RESPONSE_EXAMPLE = new RestComment(1L, 1, "A measured reply.", null,
            RestApplicationUser.RESPONSE_EXAMPLE, new Date(), new Date(), null, Lists.newArrayList(RESPONSE_EXAMPLE_NO_REPLY),
            ImmutableMap.of("editable", true, "deletable", true),
            new PropertyMap.Builder().property("key", "value").build(),
            Lists.<RestTask>newArrayList());
}
