/*
 * 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 com.android.settings.homepage.contextualcards;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Build;
import android.os.StrictMode;
import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.settingslib.utils.ThreadUtils;

/**
 * Provider stores and manages user interaction feedback for homepage contextual cards.
 */
public class CardContentProvider extends ContentProvider {

    public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider";

    public static final Uri REFRESH_CARD_URI = new Uri.Builder()
                    .scheme(ContentResolver.SCHEME_CONTENT)
                    .authority(CardContentProvider.CARD_AUTHORITY)
                    .appendPath(CardDatabaseHelper.CARD_TABLE)
                    .build();

    public static final Uri DELETE_CARD_URI = new Uri.Builder()
            .scheme(ContentResolver.SCHEME_CONTENT)
            .authority(CardContentProvider.CARD_AUTHORITY)
            .appendPath(CardDatabaseHelper.CardColumns.CARD_DISMISSED)
            .build();

    private static final String TAG = "CardContentProvider";
    /** URI matcher for ContentProvider queries. */
    private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    /** URI matcher type for cards table */
    private static final int MATCH_CARDS = 100;

    static {
        URI_MATCHER.addURI(CARD_AUTHORITY, CardDatabaseHelper.CARD_TABLE, MATCH_CARDS);
    }

    private CardDatabaseHelper mDBHelper;

    @Override
    public boolean onCreate() {
        mDBHelper = CardDatabaseHelper.getInstance(getContext());
        return true;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        final ContentValues[] cvs = {values};
        bulkInsert(uri, cvs);
        return uri;
    }

    @Override
    public int bulkInsert(Uri uri, ContentValues[] values) {
        final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
        int numInserted = 0;
        final SQLiteDatabase database = mDBHelper.getWritableDatabase();

        try {
            maybeEnableStrictMode();

            final String table = getTableFromMatch(uri);
            database.beginTransaction();

            // Here deletion first is avoiding redundant insertion. According to cl/215350754
            database.delete(table, null /* whereClause */, null /* whereArgs */);
            for (ContentValues value : values) {
                long ret = database.insert(table, null /* nullColumnHack */, value);
                if (ret != -1L) {
                    numInserted++;
                } else {
                    Log.e(TAG, "The row " + value.getAsString(CardDatabaseHelper.CardColumns.NAME)
                            + " insertion failed! Please check your data.");
                }
            }
            database.setTransactionSuccessful();
            getContext().getContentResolver().notifyChange(uri, null /* observer */);
        } finally {
            database.endTransaction();
            StrictMode.setThreadPolicy(oldPolicy);
        }
        return numInserted;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        throw new UnsupportedOperationException("delete operation not supported currently.");
    }

    @Override
    public String getType(Uri uri) {
        throw new UnsupportedOperationException("getType operation not supported currently.");
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
        try {
            maybeEnableStrictMode();

            final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
            final String table = getTableFromMatch(uri);
            queryBuilder.setTables(table);
            final SQLiteDatabase database = mDBHelper.getReadableDatabase();
            final Cursor cursor = queryBuilder.query(database,
                    projection, selection, selectionArgs, null /* groupBy */, null /* having */,
                    sortOrder);

            cursor.setNotificationUri(getContext().getContentResolver(), uri);
            return cursor;
        } finally {
            StrictMode.setThreadPolicy(oldPolicy);
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        throw new UnsupportedOperationException("update operation not supported currently.");
    }

    @VisibleForTesting
    void maybeEnableStrictMode() {
        if (Build.IS_DEBUGGABLE && ThreadUtils.isMainThread()) {
            enableStrictMode();
        }
    }

    @VisibleForTesting
    void enableStrictMode() {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().build());
    }

    @VisibleForTesting
    String getTableFromMatch(Uri uri) {
        final int match = URI_MATCHER.match(uri);
        String table;
        switch (match) {
            case MATCH_CARDS:
                table = CardDatabaseHelper.CARD_TABLE;
                break;
            default:
                throw new IllegalArgumentException("Unknown Uri format: " + uri);
        }
        return table;
    }
}
