package ru.yandex.webmaster3.core.semantic.review_business.auto.model;

import org.apache.commons.lang3.ObjectUtils;
import ru.yandex.webmaster3.core.semantic.review_business.ModelUtils;
import ru.yandex.webmaster3.core.semantic.review_business.model.Author;

import java.util.Collection;
import java.util.Date;
import java.util.List;


/**
 * Represents base review type. Include common fields for Agg and Ugc auto reviews. This class is not supposed to be used
 * as stand alone object. Only as part of Agg or Ugc auto review.
 * <p/>
 * User: Evgeny Zhoga (ezhoga@yandex-team.ru)
 * Date: 24/9/12
 * Time: 3:19 PM
 */
public abstract class BaseAutoReview {
    public static final float NORMALIZED_WORST_RATING = 1.0f;
    public static final float NORMALIZED_BEST_RATING = 5.0f;

    public abstract String getId();

    /**
     * Language of the review according to "ISO 639-3" standard.
     * It may be constructor argument of java.util.Locale
     */
    public abstract String getLanguage();

    /**
     * Permanent ID is identifier of review which is not changed between versions
     *
     * @return permanent ID of review (usually hash)
     */
    public abstract String getPermanentId();

    /**
     * Version ID is identifier of review which is changed during review modification
     *
     * @return version ID of review (usually hash)
     */
    public abstract String getVersionId();

    /**
     * Rating from user.
     * By default it's float number between 1 and 5.
     *
     * @return rating from user
     * or null if rating is undefined
     */
    public abstract Float getRating();

    /**
     * Upper bound of possible rating value.
     * Typically it should be used for scale rating.
     *
     * @return possible max rating value
     */
    public abstract Float getBestRating();

    /**
     * Lower bound of possible rating value.
     * Typically it should be used for scale rating.
     *
     * @return possible min rating value
     */
    public abstract Float getWorstRating();

    /**
     * Information about review author.
     *
     * @return info about author
     * or null if one is unavailable
     */
    public abstract Author getReviewer();

    /**
     * Date of review creation
     */
    public abstract Date getReviewedDate();

    /**
     * This optional property serves as a more detailed synopsis of the review
     * (i.e. full text representing the written opinion of the reviewer)
     * than that provided by summary.
     *
     * @return non-empty string or
     * null if description doesn't specified
     */
    public abstract String getDescription();

    /**
     * This optional property serves as a short synopsis,
     * title, or name of the review.
     *
     * @return non-empty string or
     * null if doesn't specified
     */
    public abstract String getSummary();

    /**
     * TODO what is it??
     */
    public abstract Date getWatchTime();

    /**
     * Information about review item
     * (e.g. object that review is about)
     *
     * @return info about review object
     * or null if one is unavailable
     */
    public abstract HProduct getItem();


    /**
     * Photos of reviewing object.
     *
     * @return number of strings
     * or empty collection if no photo is specified
     */
    public abstract Collection<String> getPhotos();

    /**
     * Qualities of object under review.
     *
     * @return number of strings
     * or empty collection if no pro is specified
     */
    public abstract Collection<String> getPros();

    /**
     * Shortcomings of object under review.
     *
     * @return number of strings
     * or empty collection if no contra is specified
     */
    public abstract Collection<String> getContras();

    /**
     * History of review's moderation. May be empty or null.
     */
    public abstract List<AutoModeration> getModerationHistory();

    /**
     * Indicates has review moderation or not.
     * Simply check moderation history for emptiness.
     */
    public boolean hasModeration() {
        final List<AutoModeration> history = getModerationHistory();
        return (history != null) && (!history.isEmpty());
    }

    /**
     * @return last (actual) moderation of review or null if there's no moderation.
     */
    public AutoModeration getLastModeration() {
        final List<AutoModeration> history = getModerationHistory();
        if (history == null || history.isEmpty())
            return null;
        return history.get(history.size() - 1);
    }

    public Float getNormalizedRating() {
        if (getRating() == null) {
            return null;
        } else {
            float worstRating = ObjectUtils.firstNonNull(getWorstRating(), NORMALIZED_WORST_RATING);
            float bestRating = ObjectUtils.firstNonNull(getBestRating(), NORMALIZED_BEST_RATING);
            if (getRating() >= worstRating && getRating() <= bestRating)
                return (getRating() - worstRating) *
                        (NORMALIZED_BEST_RATING - NORMALIZED_WORST_RATING) /
                        (bestRating - worstRating) + NORMALIZED_WORST_RATING;
            else return null;
        }
    }

    /**
     * Indicates whether review deleted or not.
     * Mostly used by ugc reviews. There is special field for this purpose in MySql table named 'is_deleted'.
     */
    public abstract boolean isDeleted();

    /**
     * Number of users considered review is useful.
     */
    public abstract Integer getUsefulCount();

    /**
     * Number of users considered review is useless.
     */
    public abstract Integer getUselessCount();

    @Override
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (!(o instanceof BaseAutoReview)) return false;

        final BaseAutoReview review = (BaseAutoReview) o;

        return hashCode() == review.hashCode()
                && ModelUtils.equals(getId(), review.getId())
                && ModelUtils.equals(getPermanentId(), review.getPermanentId())
                && ModelUtils.equals(getVersionId(), review.getVersionId())
                && ModelUtils.equals(getRating(), review.getRating())
                && ModelUtils.equals(getBestRating(), review.getBestRating())
                && ModelUtils.equals(getWorstRating(), review.getWorstRating())
                && ModelUtils.equals(getReviewer(), review.getReviewer())
                && ModelUtils.equals(getReviewedDate(), review.getReviewedDate())
                && ModelUtils.equals(getDescription(), review.getDescription())
                && ModelUtils.equals(getSummary(), review.getSummary())
                && ModelUtils.equals(getWatchTime(), review.getWatchTime())
                && ModelUtils.equals(getItem(), review.getItem())
                && ModelUtils.equals(getPros(), review.getPros())
                && ModelUtils.equals(getContras(), review.getContras())
                && ModelUtils.equals(getModerationHistory(), review.getModerationHistory())
                && ModelUtils.equals(isDeleted(), review.isDeleted())
                && ModelUtils.equals(getUsefulCount(), review.getUsefulCount())
                && ModelUtils.equals(getUselessCount(), review.getUselessCount())
                && ModelUtils.equals(getLanguage(), review.getLanguage())
                && ModelUtils.equals(getPhotos(), review.getPhotos())
                ;
    }

    @Override
    public int hashCode() {
        return ModelUtils.hashCode(
                getId()
                , getPermanentId()
                , getVersionId()
                , getRating()
                , getBestRating()
                , getWorstRating()
                , getReviewer()
                , getReviewedDate()
                , getDescription()
                , getSummary()
                , getWatchTime()
                , getItem()
                , getPros()
                , getContras()
                , getModerationHistory()
                , isDeleted()
                , getUsefulCount()
                , getUselessCount()
                , getLanguage()
                , getPhotos()
        );
    }

    /**
     * List of photo urls
     */
    public abstract List<String> getPhotoUrls();
}

