package com.atlassian.bitbucket.rest.task;

import com.atlassian.bitbucket.rest.comment.RestComment;
import com.atlassian.bitbucket.comment.Comment;
import com.atlassian.bitbucket.property.PropertyMap;
import com.atlassian.bitbucket.rest.annotation.JsonSurrogate;
import com.atlassian.bitbucket.rest.RestMapEntity;
import com.atlassian.bitbucket.rest.user.RestApplicationUser;
import com.atlassian.bitbucket.rest.util.RestPage;
import com.atlassian.bitbucket.rest.property.RestPropertySupport;
import com.atlassian.bitbucket.rest.util.RestDocUtils;
import com.atlassian.bitbucket.task.*;
import com.atlassian.bitbucket.user.ApplicationUser;
import org.codehaus.jackson.map.annotate.JsonSerialize;

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

@JsonSerialize
@JsonSurrogate(Task.class)
public class RestTask extends RestPropertySupport {
    public static final Function<Task, RestTask> REST_TRANSFORM = new Function<Task, RestTask>() {

        @Override
        public RestTask apply(Task input) {
            return new RestTask(input);
        }
    };

    private static final TaskAnchorVisitor<RestMapEntity> ANCHOR_REST_TRANSFORM = new TaskAnchorVisitor<RestMapEntity>() {
        @Override
        public RestMapEntity visit(@Nonnull Comment comment) {
            // Do not render the diff anchor or replies for brevity of the response.
            // Do not render tasks to avoid stack overflow. :)
            RestComment restComment = new RestComment(comment, null, null, null);
            restComment.put(ANCHOR_TYPE, TaskAnchorType.COMMENT);
            return restComment;
        }
    };

    public static final String ANCHOR = "anchor";
    public static final String ANCHOR_TYPE = "type";
    public static final String AUTHOR = "author";
    public static final String CREATED_DATE = "createdDate";
    public static final String ID = "id";
    public static final String PERMITTED_OPERATIONS = "permittedOperations";
    public static final String STATE = "state";
    public static final String TEXT = "text";

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

    public RestTask(@Nonnull Task task) {
        this(task.getAnchor().accept(ANCHOR_REST_TRANSFORM), task.getProperties(), task.getAuthor(), null, task.getCreatedDate(),
                task.getId(), task.getText(), task.getState());
        TaskOperations permittedOperations = task.getPermittedOperations();
        if (permittedOperations != null) {
            put(PERMITTED_OPERATIONS, new RestTaskOperations(permittedOperations));
        }
    }

    // Required for REST documentation
    private RestTask(RestMapEntity anchor, PropertyMap properties, ApplicationUser author, RestTaskOperations taskOperations,
                     Date createdDate, long id, String text, TaskState state) {
        super(properties);
        putIfNotEmpty(ANCHOR, anchor);
        putIfNotEmpty(AUTHOR, author == null ? Collections.emptyMap() : new RestApplicationUser(author));
        putIfNotNull(CREATED_DATE, createdDate);
        if (id > 0L) {
            put(ID, id);
        }
        putIfNotEmpty(PERMITTED_OPERATIONS, taskOperations);
        putIfNotNull(TEXT, text);
        putIfNotNull(STATE, state);
    }

    public long getAnchorId() {
        return getAnchorMap().getId();
    }

    public TaskAnchorType getAnchorType() {
        return getAnchorMap().getType();
    }

    @Nullable
    public RestApplicationUser getAuthor() {
        return RestApplicationUser.valueOf(get(AUTHOR));
    }

    @Nullable
    public Date getCreatedDate() {
        long createdDate = getLongProperty(CREATED_DATE);

        return createdDate > 0 ? new Date(createdDate) : null;
    }

    public long getId() {
        return getLongProperty(ID);
    }

    @Nullable
    public TaskState getState() {
        return get(STATE) == null ? null : getEnumProperty(STATE, TaskState.class);
    }

    @Nullable
    public String getText() {
        return getStringProperty(TEXT);
    }

    @SuppressWarnings("unchecked")
    private RestTaskAnchor getAnchorMap() {
        return new RestTaskAnchor((Map) get(ANCHOR));
    }

    // REST Documentation Examples

    private static final PropertyMap SAMPLE_TASK_PROPERTY_MAP = new PropertyMap.Builder()
            .property("diffAnchorPath", "path/to/file.txt").build();

    private static final RestMapEntity REQUEST_CREATE_TASK_ANCHOR = new RestMapEntity() { {
        put(ID, 1L);
        put(ANCHOR_TYPE, TaskAnchorType.COMMENT);
    } };

    private static final RestMapEntity RESPONSE_TASK_ANCHOR_WITH_DIFFERENT_CONTENT = new RestMapEntity(RestComment.RESPONSE_EXAMPLE_NO_REPLY) { {
        put("id", 5L);
        put("text", "This class is deprecated.  Can you please use the replacement?");
    } };

    public static final RestTask REQUEST_CREATE_TASK_EXAMPLE = new RestTask(REQUEST_CREATE_TASK_ANCHOR, null, null,
            null, null, 0L, "Fix the missing imports", null);

    public static final RestTask RESPONSE_CREATE_TASK_EXAMPLE = new RestTask(RestComment.RESPONSE_EXAMPLE_NO_REPLY,
            /* when created, tasks are not populated with attributes */ null, RestApplicationUser.RESPONSE_EXAMPLE,
            new RestTaskOperations(true, true, true), new Date(), 99L, "Fix the missing imports", null);

    public static final RestTask REQUEST_UPDATE_TASK_EXAMPLE = new RestTask(null, null, null, null, null, 99L,
            "Fix the missing and obsolete imports", null);

    public static final RestTask RESPONSE_UPDATE_TASK_EXAMPLE = new RestTask(RestComment.RESPONSE_EXAMPLE_NO_REPLY,
            SAMPLE_TASK_PROPERTY_MAP, RestApplicationUser.RESPONSE_EXAMPLE, new RestTaskOperations(true, true, true),
            new Date(), 99L, "Fix the missing and deprecated imports", TaskState.OPEN);

    public static final RestTask RESPONSE_GET_TASK_EXAMPLE = new RestTask(RestComment.RESPONSE_EXAMPLE_NO_REPLY,
            SAMPLE_TASK_PROPERTY_MAP, RestApplicationUser.RESPONSE_EXAMPLE, new RestTaskOperations(false, false, true),
            new Date(), 99L, "Resolve the merge conflicts", TaskState.OPEN);

    public static final RestPage<RestTask> RESPONSE_GET_PR_TASKS_EXAMPLE = RestDocUtils.pageOf(
            new RestTask(RestComment.RESPONSE_EXAMPLE_NO_REPLY, SAMPLE_TASK_PROPERTY_MAP,
                    RestApplicationUser.RESPONSE_EXAMPLE, new RestTaskOperations(true, true, true),
                    new Date(), 98L, "Fix typos on the constructor's arguments", TaskState.OPEN),
            new RestTask(RESPONSE_TASK_ANCHOR_WITH_DIFFERENT_CONTENT, SAMPLE_TASK_PROPERTY_MAP,
                    RestApplicationUser.RESPONSE_EXAMPLE, new RestTaskOperations(false, false, true),
                    new Date(), 99L, "Remove usage of deprecated class", TaskState.RESOLVED));

    private static class RestTaskAnchor extends RestMapEntity {

        private RestTaskAnchor(Map<String, ?> values) {
            if (values != null) {
                putAll(values);
            }
        }

        private long getId() {
            return getLongProperty(ID);
        }

        private TaskAnchorType getType() {
            return getEnumProperty(ANCHOR_TYPE, TaskAnchorType.class);
        }
    }
}
