package com.atlassian.bitbucket.rest.comment;

import com.atlassian.bitbucket.comment.AbstractAddCommentRequest;
import com.atlassian.bitbucket.comment.AddDiffCommentRequest;
import com.atlassian.bitbucket.comment.AddFileCommentRequest;
import com.atlassian.bitbucket.comment.DiffCommentAnchor;
import com.atlassian.bitbucket.commit.CommitDiscussionCommentAnchor;
import com.atlassian.bitbucket.content.DiffFileType;
import com.atlassian.bitbucket.content.DiffSegmentType;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.pull.PullRequestDiffCommentAnchor;
import com.atlassian.bitbucket.rest.RestMapEntity;
import com.atlassian.bitbucket.rest.annotation.JsonSurrogate;
import com.atlassian.bitbucket.rest.content.RestPath;
import com.atlassian.bitbucket.rest.pull.RestPullRequest;
import com.atlassian.bitbucket.rest.util.ResponseFactory;
import org.codehaus.jackson.map.annotate.JsonSerialize;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.ws.rs.WebApplicationException;
import java.util.Map;
import java.util.function.Function;

@JsonSerialize
@JsonSurrogate(DiffCommentAnchor.class)
@SuppressWarnings("unused")
public class RestDiffCommentAnchor extends RestMapEntity {

    public static final String LINE = "line";
    public static final String LINE_TYPE = "lineType";
    public static final String FILE_TYPE = "fileType";

    public static final Function<DiffCommentAnchor, RestDiffCommentAnchor> REST_TRANSFORM = RestDiffCommentAnchor::new;
    
    @SuppressWarnings("unused") //Required for Jersey
    public RestDiffCommentAnchor() {
    }

    public RestDiffCommentAnchor(Map<String, Object> values) {
        super(values);
    }

    public RestDiffCommentAnchor(DiffCommentAnchor anchor) {
        this(anchor.getFromHash(), anchor.getToHash(), anchor.getPath(), anchor.getSrcPath(),
                anchor.isLineComment() ? anchor.getLine() : null,
                anchor.isLineComment() ? anchor.getLineType() : null,
                anchor.isLineComment() ? anchor.getFileType() : null);

        if (PullRequestDiffCommentAnchor.class.isInstance(anchor)) {
            put("diffType", PullRequestDiffCommentAnchor.class.cast(anchor).getDiffType());
        } else if (CommitDiscussionCommentAnchor.class.isInstance(anchor)) {
            CommitDiscussionCommentAnchor.class.cast(anchor).getPullRequest()
                    .ifPresent(pullRequest -> put("pullRequest", new RestPullRequest(pullRequest)));
        }
    }

    // Required for REST documentation
    protected RestDiffCommentAnchor(String fromHash, String toHash, String path, String srcPath, Integer line,
                                    DiffSegmentType lineType, DiffFileType fileType) {
        this(fromHash, toHash, line, lineType, fileType);
        putIfNotNull("path", path);
        putIfNotNull("srcPath", srcPath);
    }

    // Required for REST documentation
    protected RestDiffCommentAnchor(String fromHash, String toHash, Integer line, DiffSegmentType lineType,
                                    DiffFileType fileType) {
        putIfNotNull("fromHash", fromHash);
        putIfNotNull("toHash", toHash);
        putIfNotNull(LINE, line);
        putIfNotNull(LINE_TYPE, lineType);
        putIfNotNull(FILE_TYPE, fileType);
    }

    // Required for REST documentation
    protected RestDiffCommentAnchor(String fromHash, String toHash, Integer line, DiffSegmentType lineType,
                                    DiffFileType fileType, RestPullRequest pullRequest) {
        putIfNotNull("fromHash", fromHash);
        putIfNotNull("toHash", toHash);
        putIfNotNull(LINE, line);
        putIfNotNull(LINE_TYPE, lineType);
        putIfNotNull(FILE_TYPE, fileType);
        putIfNotNull("pullRequest", pullRequest);
    }

    public DiffFileType getFileType() {
        return getEnumProperty(FILE_TYPE, DiffFileType.class);
    }

    public int getLine() {
        return getIntProperty(LINE);
    }

    public DiffSegmentType getLineType() {
        return getEnumProperty(LINE_TYPE, DiffSegmentType.class);
    }

    public RestPath getPath() {
        return getPathProperty("path");
    }

    @Nullable
    public RestPullRequest getPullRequest() {
        return RestPullRequest.valueOf(get("pullRequest"));
    }

    public RestPath getSrcPath() {
        return getPathProperty("srcPath");
    }

    public boolean hasSrcPath() {
        return containsKey("srcPath");
    }

    public boolean isLineComment() {
        return containsKey("line") && containsKey("lineType");
    }

    /**
     * @deprecated in 4.7 for removal in 5.0
     */
    @Deprecated
    public AddFileCommentRequest.Builder toFile(@Nonnull I18nService i18nService) {
        return addPaths(new AddFileCommentRequest.Builder());
    }

    /**
     * @deprecated in 4.7 for removal in 5.0
     */
    @Deprecated
    public AddDiffCommentRequest.Builder toLine(@Nonnull I18nService i18nService) {
        int line = getLine();
        if (line < 0) {
            throw new WebApplicationException(ResponseFactory.badRequest("anchor.line",
                    i18nService.getMessage("bitbucket.rest.comment.anchor.line.invalid", line))
                    .build());
        }

        DiffSegmentType lineType;
        try {
            lineType = getLineType();
            if (lineType == null) {
                throw new WebApplicationException(ResponseFactory.badRequest("anchor.lineType",
                        i18nService.getMessage("bitbucket.rest.commit.comment.anchor.lineType.empty"))
                        .build());
            }
        } catch (IllegalArgumentException e) {
            throw new WebApplicationException(ResponseFactory.badRequest("anchor.lineType",
                    i18nService.getMessage("bitbucket.rest.commit.comment.anchor.lineType.invalid", get(RestDiffCommentAnchor.LINE_TYPE)))
                    .build());
        }

        DiffFileType fileType;
        try {
            fileType = getFileType();
            if (fileType == null) {
                //If the fileType is null, default it. This retains backwards compatibility with
                //the REST API prior to supporting the fileType
                fileType = DiffFileType.forSegmentType(lineType);
                put(FILE_TYPE, fileType.name());
            }
        } catch (IllegalArgumentException e) {
            throw new WebApplicationException(ResponseFactory.badRequest("anchor.fileType",
                    i18nService.getMessage("bitbucket.rest.commit.comment.anchor.fileType.invalid", get(RestDiffCommentAnchor.FILE_TYPE)))
                    .build());
        }

        return addPaths(new AddDiffCommentRequest.Builder()
                .fileType(fileType)
                .line(line)
                .lineType(lineType));
    }

    private <B extends AbstractAddCommentRequest.AbstractBuilder<B, ?>> B addPaths(B builder) {
        RestPath srcPath = getSrcPath();

        return builder.path(getPath().toString())
                .srcPath(srcPath == null ? null : srcPath.toString());
    }

    // REST Documentation Examples

    public static final RestDiffCommentAnchor LINE_REQUEST_MINIMUM_EXAMPLE = new RestDiffCommentAnchor(null, null,
            "path/to/file", "path/to/file", 1, DiffSegmentType.CONTEXT, DiffFileType.FROM);
}
