/**
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 android.ext.services.notification;

import static android.app.NotificationManager.IMPORTANCE_MIN;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.ext.services.notification.NotificationCategorizer.Category;
import android.net.Uri;
import android.util.ArraySet;
import android.util.Log;

import java.util.Set;

public class AgingHelper {
    private final static String TAG = "AgingHelper";
    private final boolean DEBUG = false;

    private static final String AGING_ACTION = AgingHelper.class.getSimpleName() + ".EVALUATE";
    private static final int REQUEST_CODE_AGING = 1;
    private static final String AGING_SCHEME = "aging";
    private static final String EXTRA_KEY = "key";
    private static final String EXTRA_CATEGORY = "category";

    private static final int HOUR_MS = 1000 * 60 * 60;
    private static final int TWO_HOURS_MS = 2 * HOUR_MS;

    private Context mContext;
    private NotificationCategorizer mNotificationCategorizer;
    private AlarmManager mAm;
    private Callback mCallback;

    // The set of keys we've scheduled alarms for
    private Set<String> mAging = new ArraySet<>();

    public AgingHelper(Context context, NotificationCategorizer categorizer, Callback callback) {
        mNotificationCategorizer = categorizer;
        mContext = context;
        mAm = mContext.getSystemService(AlarmManager.class);
        mCallback = callback;

        IntentFilter filter = new IntentFilter(AGING_ACTION);
        filter.addDataScheme(AGING_SCHEME);
        mContext.registerReceiver(mBroadcastReceiver, filter);
    }

    // NAS lifecycle methods

    public void onNotificationSeen(NotificationEntry entry) {
        // user has strong opinions about this notification. we can't down rank it, so don't bother.
        if (entry.getChannel().hasUserSetImportance()) {
            return;
        }

        @Category int category = mNotificationCategorizer.getCategory(entry);

        // already very low
        if (category == NotificationCategorizer.CATEGORY_MIN) {
            return;
        }

        if (entry.hasSeen()) {
            if (category == NotificationCategorizer.CATEGORY_ONGOING
                    || category > NotificationCategorizer.CATEGORY_REMINDER) {
                scheduleAging(entry.getSbn().getKey(), category, TWO_HOURS_MS);
            } else {
                scheduleAging(entry.getSbn().getKey(), category, HOUR_MS);
            }

            mAging.add(entry.getSbn().getKey());
        }
    }

    public void onNotificationPosted(NotificationEntry entry) {
        cancelAging(entry.getSbn().getKey());
    }

    public void onNotificationRemoved(String key) {
        cancelAging(key);
    }

    public void onDestroy() {
        mContext.unregisterReceiver(mBroadcastReceiver);
    }

    // Aging

    private void scheduleAging(String key, @Category int category, long duration) {
        if (mAging.contains(key)) {
            // already scheduled. Don't reset aging just because the user saw the noti again.
            return;
        }
        final PendingIntent pi = createPendingIntent(key, category);
        long time = System.currentTimeMillis() + duration;
        if (DEBUG) Log.d(TAG, "Scheduling evaluate for " + key + " in ms: " + duration);
        mAm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pi);
    }

    private void cancelAging(String key) {
        final PendingIntent pi = createPendingIntent(key);
        mAm.cancel(pi);
        mAging.remove(key);
    }

    private Intent createBaseIntent(String key) {
        return new Intent(AGING_ACTION)
                .setData(new Uri.Builder().scheme(AGING_SCHEME).appendPath(key).build());
    }

    private Intent createAgingIntent(String key, @Category int category) {
        Intent intent = createBaseIntent(key);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                .putExtra(EXTRA_CATEGORY, category)
                .putExtra(EXTRA_KEY, key);
        return intent;
    }

    private PendingIntent createPendingIntent(String key, @Category int category) {
        return PendingIntent.getBroadcast(mContext,
                REQUEST_CODE_AGING,
                createAgingIntent(key, category),
                PendingIntent.FLAG_UPDATE_CURRENT);
    }

    private PendingIntent createPendingIntent(String key) {
        return PendingIntent.getBroadcast(mContext,
                REQUEST_CODE_AGING,
                createBaseIntent(key),
                PendingIntent.FLAG_UPDATE_CURRENT);
    }

    private void demote(String key, @Category int category) {
        int newImportance = IMPORTANCE_MIN;
        // TODO: Change "aged" importance based on category
        mCallback.sendAdjustment(key, newImportance);
    }

    protected interface Callback {
        void sendAdjustment(String key, int newImportance);
    }

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) {
                Log.d(TAG, "Reposting notification");
            }
            if (AGING_ACTION.equals(intent.getAction())) {
                demote(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_CATEGORY,
                        NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
            }
        }
    };
}
