package com.atlassian.bitbucket.internal.mirroring.mirror;

import com.atlassian.bitbucket.hamcrest.MatcherBuilder;
import com.atlassian.bitbucket.mirroring.mirror.IntegrationState;
import com.atlassian.bitbucket.mirroring.mirror.UpstreamAccount;
import com.atlassian.bitbucket.mirroring.mirror.UpstreamServerType;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.Optional;

import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;

/**
 * Matcher for {@link InternalUpstreamServer} objects.
 *
 * @since 4.6
 */
public class InternalUpstreamServerMatcher extends TypeSafeDiagnosingMatcher<InternalUpstreamServer> {

    private String apiBaseUrl;
    private String baseUrl;
    private Optional<String> issuerId;
    private String id;
    private Date lastSettingsChangeDate;
    private Date lastFullSyncDate;
    private String namespace;
    private Optional<String> sharedSecret;
    private IntegrationState integrationState;
    private UpstreamServerType type;
    private Optional<UpstreamAccount> account;

    public InternalUpstreamServerMatcher() {
        account = empty();
    }

    public InternalUpstreamServerMatcher(InternalUpstreamServer server) {
        apiBaseUrl = server.getApiBaseUrl();
        baseUrl = server.getBaseUrl();
        issuerId = server.getIssuerId();
        id = server.getId();
        lastSettingsChangeDate = server.getLastSettingsChangeDate();
        lastFullSyncDate = server.getLastFullSyncDate();
        namespace = server.getNamespace();
        sharedSecret = server.getSharedSecret();
        integrationState = server.getState();
        type = server.getType();
        account = server.getAccount();
    }

    public InternalUpstreamServerMatcher account(UpstreamAccount account) {
        this.account = ofNullable(account);
        return this;
    }

    public InternalUpstreamServerMatcher apiBaseUrl(String value) {
        apiBaseUrl = value;
        return this;
    }

    public InternalUpstreamServerMatcher baseUrl(String value) {
        baseUrl = value;
        return this;
    }

    public InternalUpstreamServerMatcher id(String value) {
        id = value;
        return this;
    }

    public InternalUpstreamServerMatcher issuerId(String value) {
        issuerId = ofNullable(value);
        return this;
    }

    public InternalUpstreamServerMatcher lastSettingsChangeDate(Date value) {
        lastSettingsChangeDate = value;
        return this;
    }

    public InternalUpstreamServerMatcher lastFullSyncDate(Date value) {
        lastFullSyncDate = value;
        return this;
    }

    public InternalUpstreamServerMatcher namespace(String value) {
        namespace = value;
        return this;
    }

    public InternalUpstreamServerMatcher secret(String value) {
        sharedSecret = ofNullable(value);
        return this;
    }

    public InternalUpstreamServerMatcher state(IntegrationState value) {
        integrationState = value;
        return this;
    }

    public InternalUpstreamServerMatcher type(UpstreamServerType value) {
        type = value;
        return this;
    }

    @Override
    protected boolean matchesSafely(InternalUpstreamServer item, Description mismatchDescription) {
        return new MatcherBuilder()
                .equal("account", account, item.getAccount())
                .equal("apiBaseUrl", apiBaseUrl, item.getApiBaseUrl())
                .equal("baseUrl", baseUrl, item.getBaseUrl())
                .equal("issuerId", issuerId, item.getIssuerId())
                .equal("id", id, item.getId())
                .equal("lastSettingsChangeDate", lastSettingsChangeDate, item.getLastFullSyncDate())
                .equal("lastFullSyncDate", lastFullSyncDate, item.getLastSettingsChangeDate())
                .equal("namespace", namespace, item.getNamespace())
                .equal("sharedSecret", sharedSecret, item.getSharedSecret())
                .equal("integrationState", integrationState, item.getState())
                .equal("type", type, item.getType())
                .collect(mismatchDescription);
    }

    @Override
    public void describeTo(Description description) {
        StringWriter buffer = new StringWriter();
        PrintWriter writer = new PrintWriter(buffer);
        writer.printf("{account: %s", account);
        writer.printf(", apiBaseUrl: %s", apiBaseUrl);
        writer.printf(", baseUrl: %s", baseUrl);
        writer.printf(", issuerId: %s", issuerId);
        writer.printf(", id: %s", id);
        writer.printf(", lastSettingsChangeDate: %tc", lastSettingsChangeDate);
        writer.printf(", lastFullSyncDate: %tc", lastFullSyncDate);
        writer.printf(", namespace: %s", namespace);
        writer.printf(", sharedSecret: %s", sharedSecret);
        writer.printf(", integrationState: %s", integrationState);
        writer.printf(", type: %s}", type);
        writer.close();
        description.appendText(buffer.toString());
    }
}
