package com.app.simplitend.welcome.welcomepatient.fragments.contacts.mvvm;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.ContactsContract;
import android.util.Log;

import androidx.annotation.NonNull;

import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.app.simplitend.apputils.RetrofitHelper;
import com.app.simplitend.welcome.welcomepatient.fragments.contacts.mvvm.models.ContactListResponse;
import com.app.simplitend.welcome.welcomepatient.mvvm.WelcomeApiService;
import com.app.simplitend.welcome.welcomepatient.mvvm.WelcomeContracts;
import com.app.simplitend.welcome.welcomepatient.mvvm.models.CallResponse;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class UserContactRepository {

    private static final String TAG = "UserContactRepository";

    private static final String[] PROJECTION = new String[]{
            ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
            ContactsContract.Contacts.DISPLAY_NAME,
            ContactsContract.CommonDataKinds.Phone.NUMBER,
            ContactsContract.CommonDataKinds.Phone.PHOTO_URI
    };

    private static UserContactRepository contactRepository;
    private final WelcomeApiService apiService;

    private UserContactRepository() {
        apiService = RetrofitHelper.getRetrofit().create(WelcomeApiService.class);
    }

    public static synchronized UserContactRepository getContactRepository() {
        if (contactRepository == null) {
            contactRepository = new UserContactRepository();
        }

        return contactRepository;
    }

    public void getRemoteContactList(WelcomeContracts.ContactListContracts contactListContracts, String token) {

        apiService.getContactList(token).enqueue(new Callback<CallResponse<List<ContactListResponse>>>() {
            @Override
            public void onResponse(Call<CallResponse<List<ContactListResponse>>> call, Response<CallResponse<List<ContactListResponse>>> response) {
                if (response.isSuccessful() && response.body() != null && response.body().result != null) {
                    contactListContracts.onResponse(response.body().result);
                } else if (response.body() != null && response.body().result != null) {
                    Log.e(TAG, "onResponse: get contact list" + response);
                    contactListContracts.onFailure(new Exception(response.toString()), response.body().message, response.code());
                } else {
                    Log.e(TAG, "onResponse: no success response and also response body is null");
                    contactListContracts.onFailure(new Exception("no success response and also response body is null"), "It's not you, it's us.\nPlease try again later", 1);
                }
            }

            @Override
            public void onFailure(Call<CallResponse<List<ContactListResponse>>> call, Throwable t) {
                Log.e(TAG, "onFailure: ", t);
                contactListContracts.onFailure(t, t.getMessage(), 1);
            }
        });

    }

    public void createEditContact(@NonNull String URL,
                                  Map<String, RequestBody> body,
                                  Map<String, Integer> intBody,
                                  MultipartBody.Part userImage,
                                  WelcomeContracts.CreateContactInterface createContactInterface,
                                  WelcomeContracts.UpdateContactContracts updateContactContracts,
                                  @NonNull String token) {

        apiService.createEditContact(URL, body, intBody, userImage, token).enqueue(new Callback<CallResponse<Contact>>() {
            @Override
            public void onResponse(Call<CallResponse<Contact>> call, Response<CallResponse<Contact>> response) {
                if (response.isSuccessful() && response.body() != null && response.body().result != null) {
                    if (createContactInterface != null) {
                        createContactInterface.onContactCreated(response.body().result);
                    }

                    if (updateContactContracts != null) {
                        updateContactContracts.onContactUpdated();
                    }

                } else if (response.body() != null) {
                    Log.e(TAG, "onResponse: contact create or edit contact " + response);

                    if (createContactInterface != null) {
                        createContactInterface.onContactCreateFailed(new Exception(response.toString()), response.body().message, response.code());
                    }

                    if (updateContactContracts != null) {
                        updateContactContracts.onUpdateFailed();
                    }

                } else {
                    Log.e(TAG, "onResponse: no success response and also response body is null");

                    if (createContactInterface != null) {
                        createContactInterface.onContactCreateFailed(new Exception("no success response and also response body is null"), "It's not you, it's us.\nPlease try again later", 1);
                    }

                    if (updateContactContracts != null) {
                        updateContactContracts.onUpdateFailed();
                    }

                }
            }

            @Override
            public void onFailure(Call<CallResponse<Contact>> call, Throwable t) {
                Log.e(TAG, "onFailure: create or update contact ", t);

                if (createContactInterface != null) {
                    createContactInterface.onContactCreateFailed(t, "It's not you, it's us.\nPlease try again later.", 1);
                }

                if (updateContactContracts != null) {
                    updateContactContracts.onUpdateFailed();
                }
            }
        });

    }

    public void deleteContact(int contact_id, String token,
                              @NonNull WelcomeContracts.DeleteContactInterface deleteContactInterface) {
        apiService.deleteContact(token, contact_id)
                .enqueue(new Callback<CallResponse<String>>() {
                    @Override
                    public void onResponse(Call<CallResponse<String>> call, Response<CallResponse<String>> response) {
                        if (response.isSuccessful()) {
                            deleteContactInterface.onContactDelete();
                        } else {
                            deleteContactInterface.onContactDeleteFailed();
                            Log.e(TAG, "onResponse: delete contact message " + response.message() + " " +
                                    "code " + response.code());
                        }
                    }

                    @Override
                    public void onFailure(Call<CallResponse<String>> call, Throwable t) {
                        Log.e(TAG, "onFailure: delete contact", t);
                        deleteContactInterface.onContactDeleteFailed();
                    }
                });
    }

    public HashMap<String, Set<Contact>> getContactList(Context context) {

        /*
            key -> name
            values -> different contacts with same name
         */
        HashMap<String, Set<Contact>> contactMap = new HashMap<>();

        ContentResolver cr = context.getContentResolver();

        Cursor cursor = cr.query(
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                PROJECTION,
                null, null,
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");

        if (cursor != null) {

            try {
                final int c_id_index = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
                final int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
                final int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
                final int photoIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_URI);

                while (cursor.moveToNext()) {
                    String name = cursor.getString(nameIndex);
                    String number = cursor.getString(numberIndex);
                    String c_id = cursor.getString(c_id_index);
                    String photo_uri = cursor.getString(photoIndex);

                    if (number == null) continue;
                    if (name == null || name.isEmpty()) name = "No name";
                    
                    try {
                        PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
                        Phonenumber.PhoneNumber formatted_number = phoneNumberUtil.parse(number, "US");
                        number = "+" + formatted_number.getCountryCode() + formatted_number.getNationalNumber();
                    }catch (Exception e){
                        // do nothing
                    }
                    
                    Contact contact = new Contact(name, number);
                    contact.imageUri = photo_uri;
                    
                    Set<Contact> contactList;

                    if (contactMap.containsKey(c_id)) {
                        // there is already number associate with this name
                        // thus, adding to the existing contact list against the name
                        contactList = contactMap.get(c_id);
                    } else {
                        contactList = new HashSet<>();
                    }

                    if (contactList == null) contactList = new HashSet<>();

                    if (contactList.contains(contact)){
                        // duplicate number for same contact
                        // this, duplication is due to different accounts on device like Google, Whatsapp, etc
                        continue;
                    }
                    
                    contactList.add(contact);
                    contactMap.put(c_id, contactList);
                }
            } catch (Exception e) {
                // do nothing
            } finally {
                cursor.close();
            }
        }

        getEmailForContact(context, contactMap);

        return contactMap;
    }

    private void getEmailForContact(Context context, HashMap<String, Set<Contact>> contactMap) {
        Uri emailUri = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
        String[] emailProjection = {
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.CONTACT_ID
        };

        try (Cursor emailCursor = context.getContentResolver().query(emailUri, emailProjection, null, null, null)) {

            if (emailCursor != null) {
                final int email_index = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);
                final int c_id_index = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.CONTACT_ID);
                final int count = emailCursor.getCount();

                while (emailCursor.moveToNext())
                {
                    String email = emailCursor.getString(email_index);
                    String c_id = emailCursor.getString(c_id_index);

                    if (contactMap.containsKey(c_id)){
                        Set<Contact> contacts = contactMap.get(c_id);
                        if (contacts != null){
                            for (Contact contact: contacts){
                                if (contact != null){
                                    contact.email_address = email;
                                }
                            }
                        }
                    }
                }

            }
        }catch (Exception e){
            Log.e(TAG, "getEmailForContact: ", e);
        }
    }

}
