/*
 * Copyright (C) 2017 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.emergency.preferences;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.ContextWrapper;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;

import com.android.emergency.ContactTestUtils;
import com.android.emergency.EmergencyContactManager;
import com.android.emergency.PreferenceKeys;

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

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;

/** Unit tests for {@link EmergencyContactsPreference}. */
@RunWith(RobolectricTestRunner.class)
public class EmergencyContactsPreferenceTest {
    @Mock private PackageManager mPackageManager;
    @Mock private PreferenceManager mPreferenceManager;
    @Mock private SharedPreferences mSharedPreferences;
    @Mock private EmergencyContactsPreference.ContactValidator mContactValidator;
    @Mock private ContactPreference.ContactFactory mContactFactory;
    private ContextWrapper mContext;
    private EmergencyContactsPreference mPreference;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        when(mPreferenceManager.getSharedPreferences()).thenReturn(mSharedPreferences);

        mContext = spy(RuntimeEnvironment.application);
        doReturn(mPackageManager).when(mContext).getPackageManager();

        mPreference = spy(new EmergencyContactsPreference(RuntimeEnvironment.application,
                    null /* attrs */, mContactValidator, mContactFactory));

        PreferenceGroup prefRoot = spy(new PreferenceScreen(mContext, null /* attrs */));
        when(prefRoot.getPreferenceManager()).thenReturn(mPreferenceManager);
        prefRoot.addPreference(mPreference);
    }

    @Test
    public void testDefaultProperties() {
        assertThat(mPreference.isPersistent()).isTrue();
        assertThat(mPreference.isNotSet()).isTrue();
        assertThat(mPreference.getEmergencyContacts()).isEmpty();
        assertThat(mPreference.getPreferenceCount()).isEqualTo(0);
    }

    @Test
    public void testAddAndRemoveEmergencyContact() throws Throwable {
        final String name = "Jane";
        final String phoneNumber = "456";

        when(mContactValidator.isValidEmergencyContact(any(), any())).thenReturn(true);

        EmergencyContactManager.Contact contact = mock(EmergencyContactManager.Contact.class);
        when(mContactFactory.getContact(any(), any())).thenReturn(contact);
        when(contact.getName()).thenReturn(name);
        when(contact.getPhoneNumber()).thenReturn(phoneNumber);

        Uri uri = mock(Uri.class);
        when(contact.getPhoneUri()).thenReturn(uri);

        mPreference.addNewEmergencyContact(uri);

        assertThat(mPreference.getEmergencyContacts()).hasSize(1);
        assertThat(mPreference.getPreferenceCount()).isEqualTo(1);
        ContactPreference contactPreference = (ContactPreference) mPreference.getPreference(0);

        assertThat(contactPreference.getPhoneUri()).isEqualTo(uri);
        assertThat(contactPreference.getTitle()).isEqualTo(name);
        assertThat((String) contactPreference.getSummary()).contains(phoneNumber);

        mPreference.onRemoveContactPreference((ContactPreference) mPreference.getPreference(0));

        assertThat(mPreference.getEmergencyContacts()).isEmpty();
        assertThat(mPreference.getPreferenceCount()).isEqualTo(0);
    }

    @Test
    public void testReloadFromPreference() throws Throwable {
        final String nameJane = "Jane";
        final String phoneNumberJane = "456";
        Uri contactUriJane = Uri.parse("tel:" + phoneNumberJane);
        EmergencyContactManager.Contact contactJane = mock(EmergencyContactManager.Contact.class);
        when(mContactFactory.getContact(any(), eq(contactUriJane))).thenReturn(contactJane);
        when(contactJane.getName()).thenReturn(nameJane);
        when(contactJane.getPhoneNumber()).thenReturn(phoneNumberJane);
        when(contactJane.getPhoneUri()).thenReturn(contactUriJane);

        final String nameJohn = "John";
        final String phoneNumberJohn = "123";
        Uri contactUriJohn = Uri.parse("tel:" + phoneNumberJohn);
        EmergencyContactManager.Contact contactJohn = mock(EmergencyContactManager.Contact.class);
        when(mContactFactory.getContact(any(), eq(contactUriJohn))).thenReturn(contactJohn);
        when(contactJohn.getName()).thenReturn(nameJohn);
        when(contactJohn.getPhoneNumber()).thenReturn(phoneNumberJohn);
        when(contactJohn.getPhoneUri()).thenReturn(contactUriJohn);

        final List<Uri> emergencyContacts = new ArrayList<>();
        emergencyContacts.add(contactUriJane);
        emergencyContacts.add(contactUriJohn);
        mPreference.setEmergencyContacts(emergencyContacts);

        assertThat(mPreference.getEmergencyContacts().size()).isEqualTo(2);
        assertThat(mPreference.getPreferenceCount()).isEqualTo(2);

        // "Delete" Jane by reloading from preferences. The mock SharedPreferences still have both
        // contacts stored, but the validator only believes John is valid.
        mPreference.setKey(PreferenceKeys.KEY_EMERGENCY_CONTACTS);
        when(mSharedPreferences.getString(eq(mPreference.getKey()), any()))
                .thenReturn(mPreference.serialize(emergencyContacts));
        when(mContactValidator.isValidEmergencyContact(any(), eq(contactUriJane)))
                .thenReturn(false);
        when(mContactValidator.isValidEmergencyContact(any(), eq(contactUriJohn))).thenReturn(true);
        // Override the preference's persist behavior, to avoid EmergencyContactsPreference
        // attempting to write to SharedPreferences. (Preference's default behavior is unmockable.)
        doNothing().when(mPreference).persistEmergencyContacts(any());
        mPreference.reloadFromPreference();

        // Assert the only remaining contact is John
        assertThat(mPreference.getEmergencyContacts()).hasSize(1);
        assertThat(mPreference.getPreferenceCount()).isEqualTo(1);
        ContactPreference contactPreference = (ContactPreference) mPreference.getPreference(0);
        assertThat(contactPreference.getPhoneUri()).isEqualTo(contactUriJohn);
    }
}
