package com.atlassian.bitbucket.internal.scm.git.lfs.rest.model;

import com.atlassian.bitbucket.internal.scm.git.lfs.BadBatchResponseException;
import com.atlassian.bitbucket.internal.scm.git.lfs.model.OperationType;
import com.atlassian.bitbucket.internal.scm.git.lfs.model.ResponseAction;
import com.atlassian.bitbucket.rest.RestMapEntity;
import org.codehaus.jackson.map.annotate.JsonSerialize;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * REST model for a set of actions.
 *
 * An action should either be:
 * <ul>
 *     <li>download</li>
 *     <li>upload</li>
 *     <li>upload + verify</li>
 * </ul>
 *
 * However this class does not enforce any such constraints. An example of the JSON representation
 * for the multi-action case is:
 * <pre>
 * {
 *     "actions": {
 *         "upload": {
 *             "href": "https://bitbucket.example.com/...."
 *         },
 *         "verify": {
 *             "href": "https://bitbucket.example.com/...."
 *         },
 *     }
 * }
 * </pre>
 */
@JsonSerialize
public class RestResponseActions extends RestMapEntity {

    private static final String DOWNLOAD_KEY = "download";
    private static final String UPLOAD_KEY = "upload";
    private static final String VERIFY_KEY = "verify";

    public RestResponseActions(@Nonnull Collection<ResponseAction> actions) {
        actions.forEach(action ->
                put(keyFor(action.getType()), new RestResponseAction(action))
        );
    }

    protected RestResponseActions(@Nonnull Map<String, Object> values) {
        super(values);
    }

    @Nonnull
    public static RestResponseActions valueOf(@Nonnull Map<String, Object> values) {
        return new RestResponseActions(values);
    }

    /**
     * @return a list of response actions. This may be an empty list; for example if an upload request is
     * made for a file that already exists on the server, no action will be returned.
     * @throws BadBatchResponseException if the response is invalid
     */
    @Nonnull
    public List<ResponseAction> fromRest() {
        List<ResponseAction> actions = new ArrayList<>();
        addActionIfPresent(DOWNLOAD_KEY, OperationType.DOWNLOAD, actions);
        addActionIfPresent(UPLOAD_KEY, OperationType.UPLOAD, actions);
        addActionIfPresent(VERIFY_KEY, OperationType.VERIFY, actions);
        return actions;
    }

    private static String keyFor(OperationType type) {
        switch (type) {
            case DOWNLOAD:
                return DOWNLOAD_KEY;
            case UPLOAD:
                return UPLOAD_KEY;
            case VERIFY:
                return VERIFY_KEY;
            default:
                throw new RuntimeException("Unhandled action type: " + type.name());
        }
    }

    @SuppressWarnings("unchecked")
    private void addActionIfPresent(String key, OperationType type, List<ResponseAction> actions) {
        Map<String, Object> values = (Map) get(key);
        if (values != null) {
            RestResponseAction action = RestResponseAction.valueOf(values);

            String href = action.getHref()
                    .orElseThrow(() -> new BadBatchResponseException("Action is missing href attribute"));

            ResponseAction responseAction = new ResponseAction(type, href, action.getHeader());
            actions.add(responseAction);
        }
    }
}
