/*
 * Copyright 2014 sonaive.com. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sonaive.v2ex.io;

import android.content.ContentProviderOperation;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.text.TextUtils;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.sonaive.v2ex.io.model.Review;
import com.sonaive.v2ex.provider.V2exContract;
import com.sonaive.v2ex.sync.api.Api;
import com.sonaive.v2ex.util.ModelUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import static com.sonaive.v2ex.util.LogUtils.LOGD;
import static com.sonaive.v2ex.util.LogUtils.LOGE;
import static com.sonaive.v2ex.util.LogUtils.LOGW;
import static com.sonaive.v2ex.util.LogUtils.makeLogTag;

/**
 * Created by liutao on 12/15/14.
 */
public class ReviewsHandler extends JSONHandler {

    private static final String TAG = makeLogTag(ReviewsHandler.class);

    private HashMap<String, Review> mReviews = new HashMap<>();

    private int page = 1;

    private String topicId;

    public ReviewsHandler(Context context) {
        super(context);
    }

    @Override
    public void makeContentProviderOperations(ArrayList<ContentProviderOperation> list) {
        Uri uri = V2exContract.Reviews.CONTENT_URI;
        HashMap<String, String> reviewHashcodes = loadReviewHashcodes();
        HashSet<String> reviewsToKeep = new HashSet<>();
        boolean isIncrementalUpdate = reviewHashcodes != null && reviewHashcodes.size() > 0;

        if (isIncrementalUpdate) {
            LOGD(TAG, "Doing incremental update for reviews.");
        } else {
            LOGD(TAG, "Doing FULL (non incremental) update for reviews.");
            list.add(ContentProviderOperation.newDelete(uri).build());
        }

        int updatedReviews = 0;
        for (Review review : mReviews.values()) {
            String hashCode = review.getImportHashcode();
            reviewsToKeep.add(String.valueOf(review.id));

            // add review, if necessary
            if (!isIncrementalUpdate || !reviewHashcodes.containsKey(String.valueOf(review.id)) ||
                    !reviewHashcodes.get(String.valueOf(review.id)).equals(hashCode)) {
                ++updatedReviews;
                boolean isNew = !isIncrementalUpdate || !reviewHashcodes.containsKey(String.valueOf(review.id));
                buildReview(isNew, review, list);
            }
        }

        int deletedReviews = 0;
        if (isIncrementalUpdate) {
            for (String posterId : reviewHashcodes.keySet()) {
                if (!reviewsToKeep.contains(posterId)) {
                    buildDeleteOperation(posterId, list);
                    ++deletedReviews;
                }
            }
        }
        LOGD(TAG, "Reviews: " + (isIncrementalUpdate ? "INCREMENTAL" : "FULL") + " update. " +
                updatedReviews + " to update, " + deletedReviews + " to delete, New total: " + mReviews.size());
    }

    @Override
    public void process(JsonElement element) {
        if (element.isJsonArray()) {
            for (Review review : new Gson().fromJson(element, Review[].class)) {
                mReviews.put(String.valueOf(review.id), review);
            }
        }
        // Due to api hasn't supported pagination.The following code will be commented.
//        if (mReviews.size() > 0) {
//            EventBus.getDefault().postSticky(new UpdateLoadingStateEvent(LoadingStatus.FINISH));
//        } else {
//            EventBus.getDefault().postSticky(new UpdateLoadingStateEvent(LoadingStatus.NO_MORE_DATA));
//        }
    }

    @Override
    public String getBody(Bundle data) {
        if (data != null) {
            String requestParams = data.getString(Api.ARG_API_PARAMS);
            JsonObject jo = new Gson().fromJson(requestParams, JsonObject.class);
            JsonElement topicIdJsonElement = jo.get("topic_id");
            if (topicIdJsonElement != null) {
                topicId = topicIdJsonElement.getAsString();
            }
            return data.getString(Api.ARG_RESULT);
        }
        return "";
    }

    private void buildReview(boolean isInsert, Review review,
                             ArrayList<ContentProviderOperation> list) {
        Uri allReviewsUri = V2exContract.Reviews.CONTENT_URI;
        Uri thisReviewUri = V2exContract.Reviews.buildReviewTopicUri(String.valueOf(review.id));

        ContentProviderOperation.Builder builder;
        if (isInsert) {
            builder = ContentProviderOperation.newInsert(allReviewsUri);
        } else {
            builder = ContentProviderOperation.newUpdate(thisReviewUri);
        }

        if (TextUtils.isEmpty(String.valueOf(review.id))) {
            LOGW(TAG, "Ignoring feed with missing feed ID.");
            return;
        }

        list.add(builder.withValue(V2exContract.Reviews.REVIEW_ID, review.id)
                .withValue(V2exContract.Reviews.REVIEW_TOPIC_ID, topicId)
                .withValue(V2exContract.Reviews.REVIEW_THANKS, review.thanks)
                .withValue(V2exContract.Reviews.REVIEW_CONTENT, review.content)
                .withValue(V2exContract.Reviews.REVIEW_CONTENT_RENDERED, review.content_rendered)
                .withValue(V2exContract.Reviews.REVIEW_MEMBER, ModelUtils.serializeMember(review.member))
                .withValue(V2exContract.Reviews.REVIEW_CREATED, review.created)
                .withValue(V2exContract.Reviews.REVIEW_LAST_MODIFIED, review.last_modified)
                .withValue(V2exContract.Reviews.REVIEW_IMPORT_HASHCODE, review.getImportHashcode())
                .build());
    }

    private void buildDeleteOperation(String reviewId, ArrayList<ContentProviderOperation> list) {
        Uri uri = V2exContract.Reviews.buildReviewTopicUri(reviewId);
        list.add(ContentProviderOperation.newDelete(uri).build());
    }

    private HashMap<String, String> loadReviewHashcodes() {
//        int limit = page * Config.PAGE_SIZE;
//        int offset = (page - 1) * Config.PAGE_SIZE;
//        // Put the limit clause as a query parameter using the syntax 'limit = offset, limit'
//        Uri uri = V2exContract.Reviews.CONTENT_URI.buildUpon().encodedQuery("limit=" + offset + "," + limit).build();
        Uri uri = V2exContract.Reviews.CONTENT_URI;
        Cursor cursor = mContext.getContentResolver().query(uri, ReviewHashcodeQuery.PROJECTION,
                "review_topic_id = ?", new String[] {topicId}, V2exContract.Reviews.REVIEW_LAST_MODIFIED + " DESC");
        if (cursor == null) {
            LOGE(TAG, "Error querying REVIEW hashcodes (got null cursor)");
            return null;
        }
        if (cursor.getCount() < 1) {
            LOGE(TAG, "Error querying REVIEW hashcodes (no records returned)");
            cursor.close();
            return null;
        }
        HashMap<String, String> result = new HashMap<>();
        while (cursor.moveToNext()) {
            String reviewId = cursor.getString(ReviewHashcodeQuery.REVIEW_ID);
            String hashcode = cursor.getString(ReviewHashcodeQuery.REVIEW_IMPORT_HASHCODE);
            result.put(reviewId, hashcode == null ? "" : hashcode);
        }
        cursor.close();
        return result;
    }

    private interface ReviewHashcodeQuery {
        String[] PROJECTION = {
                BaseColumns._ID,
                V2exContract.Reviews.REVIEW_ID,
                V2exContract.Reviews.REVIEW_IMPORT_HASHCODE
        };
        final int _ID = 0;
        final int REVIEW_ID = 1;
        final int REVIEW_IMPORT_HASHCODE = 2;
    }
}