package com.atlassian.bitbucket.internal.mirroring.upstream.dao;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.bitbucket.ao.AbstractAoDao;
import com.atlassian.bitbucket.internal.mirroring.MirroringRequest;
import com.atlassian.bitbucket.internal.mirroring.MirroringRequestState;
import com.atlassian.bitbucket.internal.mirroring.upstream.MirroringRequestSearchRequest;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import net.java.ao.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import java.util.Date;
import java.util.Map;

import static com.atlassian.bitbucket.ao.AoUtils.newQuery;
import static com.atlassian.bitbucket.internal.mirroring.MirroringRequestState.PENDING;
import static com.atlassian.bitbucket.internal.mirroring.dao.AoConstants.*;
import static com.atlassian.bitbucket.internal.mirroring.upstream.dao.AoMirroringRequest.ALL_COLUMNS;
import static com.google.common.base.Preconditions.checkArgument;

@Component
public class AoMirroringRequestDao extends AbstractAoDao implements MirroringRequestDao {

    @Autowired
    public AoMirroringRequestDao(ActiveObjects ao) {
        super(ao);
    }

    @Override
    public int countInState(@Nonnull MirroringRequestState state) {
        return ao.count(AoMirroringRequest.class, newQuery().where(STATE_COLUMN + " = ?", state));
    }

    @Nonnull
    @Override
    public AoMirroringRequest create(@Nonnull MirroringRequest request) {
        Map<String, Object> params = ImmutableMap.<String, Object>builder()
                .put(MIRROR_ID_COLUMN, request.getMirrorId())
                .put(DESCRIPTOR_URL_COLUMN, request.getAddonDescriptorUri().toString())
                .put(BASE_URL_COLUMN, request.getBaseUrl())
                .put(MIRROR_NAME_COLUMN, request.getMirrorName())
                .put(PRODUCT_TYPE_COLUMN, request.getProductType())
                .put(PRODUCT_VERSION_COLUMN, request.getProductVersion())
                .put(STATE_COLUMN, PENDING)
                .put(CREATED_DATE_COLUMN, new Date()).build();
        return ao.create(AoMirroringRequest.class, params);
    }

    @Override
    public boolean delete(int id) {
        AoMirroringRequest request = ao.get(AoMirroringRequest.class, id);
        if (request != null) {
            ao.delete(request);
            return true;
        }
        return false;
    }

    @Nonnull
    @Override
    public Page<AoMirroringRequest> findAll(@Nonnull PageRequest pageRequest) {
        return pageQuery(AoMirroringRequest.class, newQuery(ALL_COLUMNS).order(MIRROR_NAME_COLUMN + " asc"), pageRequest);
    }

    @Nonnull
    @Override
    public Page<AoMirroringRequest> findAll(MirroringRequestSearchRequest searchRequest, PageRequest pageRequest) {
        Query query = newQuery(ALL_COLUMNS).order(MIRROR_NAME_COLUMN + " asc");
        MirroringRequestState state = searchRequest.getState();
        if (state != null) {
            query.where(STATE_COLUMN + " = ?", state);
        }
        return pageQuery(AoMirroringRequest.class, query, pageRequest);
    }

    @Nonnull
    @Override
    public Page<AoMirroringRequest> findByMirrorId(@Nonnull String mirrorId, @Nonnull PageRequest pageRequest) {
        return pageQuery(AoMirroringRequest.class, newQuery(ALL_COLUMNS)
                .where(MIRROR_ID_COLUMN + " = ?", mirrorId)
                .order(MIRROR_NAME_COLUMN + " asc"),
                pageRequest);
    }

    @Override
    public AoMirroringRequest getById(int id) {
        return ao.get(AoMirroringRequest.class, id);
    }

    @Nonnull
    @Override
    public AoMirroringRequest resolve(int id, @Nonnull MirroringRequestState resolution, int resolverUserId) {
        Preconditions.checkNotNull(resolution, "resolution");
        checkArgument(resolution != PENDING, "You cannot resolve a request with the 'PENDING' resolution");

        AoMirroringRequest request = getByIdOrFail(id);
        request.setState(resolution);
        request.setResolverId(resolverUserId);
        request.setResolvedDate(new Date());
        request.save();
        return request;
    }

    private AoMirroringRequest getByIdOrFail(int id) {
        AoMirroringRequest request = getById(id);
        checkArgument(request != null, "No mirroring request exists for id %s", id);
        return request;
    }
}
