package com.atlassian.bitbucket.hamcrest;

import com.atlassian.crowd.embedded.api.User;
import com.atlassian.stash.internal.user.InternalNormalUser;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;

import java.time.Instant;
import java.time.ZoneId;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;

import static java.lang.String.format;

public class InternalNormalUserMatcher extends TypeSafeDiagnosingMatcher<InternalNormalUser> {

    private Instant deletedTime;
    private Locale locale;
    private String name;
    private String slug;
    private ZoneId timezone;
    private User backingUser;

    public InternalNormalUserMatcher() {
    }

    public InternalNormalUserMatcher(InternalNormalUser user) {
        name = user.getName();
        slug = user.getSlug();
        timezone = user.getTimeZone();
        locale = user.getLocale();
        deletedTime = user.getDeletedDate() == null ? null : user.getDeletedDate().toInstant();
        backingUser = user.getBackingCrowdUser();
    }

    public InternalNormalUserMatcher backingUser(User value) {
        backingUser = value;
        return this;
    }

    public InternalNormalUserMatcher locale(Locale value) {
        locale = value;
        return this;
    }

    public InternalNormalUserMatcher name(String value) {
        name = value;
        return this;
    }

    public InternalNormalUserMatcher slug(String value) {
        slug = value;
        return this;
    }

    public InternalNormalUserMatcher timeZone(ZoneId value) {
        timezone = value;
        return this;
    }

    public InternalNormalUserMatcher deletedTime(Instant value) {
        deletedTime = value;
        return this;
    }

    public InternalNormalUserMatcher deletedTime(Date value) {
        deletedTime = value == null ? null : value.toInstant();
        return this;
    }

    @Override
    protected boolean matchesSafely(InternalNormalUser item, Description mismatchDescription) {
        if (Objects.equals(item.getName(), name) &&
                Objects.equals(item.getLocale(), locale) &&
                Objects.equals(item.getSlug(), slug) &&
                Objects.equals(item.getTimeZone(), timezone) &&
                Objects.equals(item.getBackingCrowdUser(), backingUser) &&
                Objects.equals(toInstant(item.getDeletedDate()), deletedTime)) {
            return true;
        } else {
            long deleted = 0;
            if (item.getDeletedDate() != null) {
                deleted = item.getDeletedDate().getTime();
            }

            mismatchDescription.appendValue(format("User(%s[%s], locale=%s, tz=%s, deletedTime=%d, backingUser=%s)",
                    item.getName(), item.getSlug(), item.getLocale(), item.getTimeZone(), deleted,
                    item.getBackingCrowdUser()));
            return false;
        }
    }

    @Override
    public void describeTo(Description description) {
        description.appendValue(toString());
    }

    @Override
    public String toString() {
        long deleted = 0;
        if (deletedTime != null) {
            deleted = deletedTime.toEpochMilli();
        }
        return format("User(%s[%s], locale=%s, tz=%s, deletedTime=%d, backingUser=%s)",
                name, slug, locale, timezone, deleted, backingUser);
    }

    private Instant toInstant(Date date) {
        return date == null ? null : date.toInstant();
    }
}
