package com.android.settings.search;

import android.content.Context;
import android.provider.SearchIndexableResource;
import android.util.ArraySet;
import com.android.settings.DateTimeSettings;
import com.android.settings.R;
import com.android.settings.SecuritySettings;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.core.codeinspection.CodeInspector;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.search2.DatabaseIndexingUtils;
import com.android.settings.testutils.XmlTestUtils;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.google.common.truth.Truth.assertThat;

@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
        assetDir = "/tests/robotests/assets")
public class DataIntegrityTest {

    @Test
    @Config(shadows = {
            SettingsShadowResources.class,
            SettingsShadowResources.SettingsShadowTheme.class,
    })
    public void testIndexableResources_uniqueKeys() {
        final Context context = RuntimeEnvironment.application;
        // Aggregation of all keys
        final Set<String> masterKeys = new ArraySet<>();
        // Aggregation of the incorrectly duplicate keys
        final Set<String> duplicateKeys = new ArraySet<>();
        // Keys for a specific page
        final Set<String> pageKeys = new ArraySet<>();
        // List of all Xml preferences
        final Set<Integer> xmlList = new ArraySet<>();
        // Duplicates we know about.
        List<String> grandfatheredKeys = new ArrayList<>();
        CodeInspector.initializeGrandfatherList(grandfatheredKeys,
                "whitelist_duplicate_index_key");

        // Get a list of all Xml.
        for (SearchIndexableResource val : SearchIndexableResources.values()) {
            final int xmlResId = val.xmlResId;
            if (xmlResId != 0) {
                xmlList.add(xmlResId);
            } else {
                // Take class and get all keys
                final Class clazz = DatabaseIndexingUtils.getIndexableClass(val.className);

                // Skip classes that are invalid or cannot be mocked. Add them as special Xml below.
                if (clazz == null
                        || clazz == DateTimeSettings.class
                        || clazz == DataUsageSummary.class
                        || clazz == SecuritySettings.class) {
                    continue;
                }

                Indexable.SearchIndexProvider provider = DatabaseIndexingUtils
                        .getSearchIndexProvider(clazz);

                if (provider == null) {
                    continue;
                }

                List<SearchIndexableResource> subXml =
                        provider.getXmlResourcesToIndex(context, true);

                if (subXml == null) {
                    continue;
                }
                for (SearchIndexableResource resource : subXml) {
                    final int subXmlResId = resource.xmlResId;
                    if (subXmlResId != 0) {
                        xmlList.add(subXmlResId);
                    }
                }
            }
        }
        addSpecialXml(xmlList);

        // Get keys from all Xml and check for duplicates.
        for (Integer xmlResId : xmlList) {
            // Get all keys to be indexed
            final List<String> prefKeys = XmlTestUtils.getKeysFromPreferenceXml(context, xmlResId);
            pageKeys.addAll(prefKeys);
            // Find all already-existing keys.
            pageKeys.retainAll(masterKeys);
            // Keep list of offending duplicate keys.
            duplicateKeys.addAll(pageKeys);
            // Add all keys to master key list.
            masterKeys.addAll(prefKeys);
            pageKeys.clear();
        }
        assertThat(duplicateKeys).containsExactlyElementsIn(grandfatheredKeys);
    }

    /**
     * Add XML preferences from Fragments which have issues being instantiated in robolectric.
     */
    private void addSpecialXml(Set<Integer> xmlList) {
        xmlList.add(R.xml.date_time_prefs);
        xmlList.add(R.xml.data_usage);
        xmlList.add(R.xml.data_usage_cellular);
        xmlList.add(R.xml.data_usage_wifi);
        xmlList.add(R.xml.security_settings_misc);
    }


}
