package com.lemenzo.messenger;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Typeface;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.telephony.PhoneNumberUtils;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import com.lemenzo.messenger.ui.Ui;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public final class ContactPickerActivity extends Activity {
    public static final String EXTRA_NAME = "name";
    public static final String EXTRA_NUMBER = "number";
    public static final String EXTRA_NAMES = "names";
    public static final String EXTRA_NUMBERS = "numbers";
    public static final String EXTRA_CONTACT_IDS = "contact_ids";
    public static final String EXTRA_PRESELECTED_NAMES = "preselected_names";
    public static final String EXTRA_PRESELECTED_NUMBERS = "preselected_numbers";
    public static final String EXTRA_PRESELECTED_CONTACT_IDS = "preselected_contact_ids";

    private final ExecutorService io = Executors.newSingleThreadExecutor();
    private final ArrayList<ContactEntry> all = new ArrayList<>();
    private final ArrayList<ContactEntry> shown = new ArrayList<>();
    private ContactAdapter adapter;
    private EditText search;
    private TextView empty;
    private TextView selectedCount;
    private TextView done;
    private LinearLayout content;
    private ListView contactsList;
    private String query = "";
    private final LinkedHashMap<Long, SelectedRecipient> selected = new LinkedHashMap<>();

    static final class ContactEntry {
        final long id;
        final String name;
        ContactEntry(long id, String name) {
            this.id = id;
            this.name = name;
        }
    }

    static final class NumberEntry {
        final String number;
        final String label;
        NumberEntry(String number, String label) {
            this.number = number;
            this.label = label;
        }
    }

    static final class SelectedRecipient {
        final long contactId;
        final String name;
        final String number;
        SelectedRecipient(long contactId, String name, String number) {
            this.contactId = contactId;
            this.name = name;
            this.number = number;
        }
    }

    @Override public void onCreate(Bundle state) {
        super.onCreate(state);
        restorePreselection();
        build();
        loadContacts();
    }

    @Override protected void onDestroy() {
        io.shutdownNow();
        super.onDestroy();
    }

    private void build() {
        LinearLayout root = new LinearLayout(this);
        root.setOrientation(LinearLayout.VERTICAL);
        root.setBackgroundColor(getColor(R.color.lemenzo_background));
        root.setPadding(Ui.dp(root, 18), Ui.dp(root, 8), Ui.dp(root, 18), Ui.dp(root, 12));
        Ui.applySafeInsets(root);

        LinearLayout header = new LinearLayout(this);
        header.setGravity(Gravity.CENTER_VERTICAL);
        ImageButton back = Ui.iconButton(this, R.drawable.ic_back, getString(R.string.navigate_back));
        back.setOnClickListener(v -> finish());
        header.addView(back, new LinearLayout.LayoutParams(Ui.dp(root, 48), Ui.dp(root, 54)));

        TextView title = Ui.text(this, getString(R.string.choose_recipients), 23, true);
        header.addView(title, new LinearLayout.LayoutParams(0, Ui.dp(root, 56), 1f));

        done = Ui.text(this, getString(R.string.done), 15, true);
        done.setTextColor(getColor(R.color.lemenzo_blue));
        done.setGravity(Gravity.CENTER);
        done.setPadding(Ui.dp(root, 10), 0, Ui.dp(root, 4), 0);
        done.setOnClickListener(v -> finishSelection());
        header.addView(done, new LinearLayout.LayoutParams(-2, Ui.dp(root, 54)));
        root.addView(header);

        LinearLayout searchShell = new LinearLayout(this);
        searchShell.setGravity(Gravity.CENTER_VERTICAL);
        searchShell.setBackgroundResource(R.drawable.search_background);
        searchShell.setPadding(Ui.dp(root, 12), 0, Ui.dp(root, 12), 0);

        ImageButton searchIcon = Ui.iconButton(this, R.drawable.ic_search, getString(R.string.search_contacts));
        searchIcon.setClickable(false);
        searchIcon.setFocusable(false);
        searchShell.addView(searchIcon, new LinearLayout.LayoutParams(Ui.dp(root, 42), Ui.dp(root, 50)));

        search = new EditText(this);
        search.setSingleLine(true);
        search.setTextSize(16);
        search.setTextColor(getColor(R.color.lemenzo_text_primary));
        search.setHintTextColor(getColor(R.color.lemenzo_text_muted));
        search.setHint(R.string.search_contacts);
        search.setBackgroundColor(0x00000000);
        search.setPadding(Ui.dp(root, 2), 0, 0, 0);
        searchShell.addView(search, new LinearLayout.LayoutParams(0, Ui.dp(root, 52), 1f));

        LinearLayout.LayoutParams searchLp = new LinearLayout.LayoutParams(-1, Ui.dp(root, 52));
        searchLp.setMargins(0, Ui.dp(root, 4), 0, Ui.dp(root, 12));
        root.addView(searchShell, searchLp);

        selectedCount = Ui.text(this, "", 13, true);
        selectedCount.setTextColor(getColor(R.color.lemenzo_blue));
        selectedCount.setPadding(Ui.dp(root, 6), 0, Ui.dp(root, 6), Ui.dp(root, 8));
        root.addView(selectedCount, new LinearLayout.LayoutParams(-1, -2));
        updateSelectionUi();

        content = new LinearLayout(this);
        content.setOrientation(LinearLayout.VERTICAL);
        root.addView(content, new LinearLayout.LayoutParams(-1, 0, 1f));

        contactsList = new ListView(this);
        ListView list = contactsList;
        list.setDivider(null);
        list.setDividerHeight(0);
        list.setCacheColorHint(0x00000000);
        list.setBackgroundColor(0x00000000);
        list.setClipToPadding(false);
        list.setPadding(0, Ui.dp(root, 4), 0, Ui.dp(root, 12));
        adapter = new ContactAdapter();
        list.setAdapter(adapter);
        list.setOnItemClickListener((p, v, pos, id) -> chooseContact(shown.get(pos)));
        content.addView(list, new LinearLayout.LayoutParams(-1, 0, 1f));

        empty = Ui.text(this, getString(R.string.no_contacts_found), 15, false);
        empty.setTextColor(getColor(R.color.lemenzo_text_secondary));
        empty.setGravity(Gravity.CENTER);
        empty.setVisibility(View.GONE);
        content.addView(empty, new LinearLayout.LayoutParams(-1, Ui.dp(root, 64)));

        search.addTextChangedListener(new TextWatcher() {
            public void beforeTextChanged(CharSequence s, int st, int c, int a) {}
            public void onTextChanged(CharSequence s, int st, int b, int c) {
                query = s == null ? "" : s.toString();
                filter();
            }
            public void afterTextChanged(Editable e) {}
        });

        setContentView(root);
    }

    private void restorePreselection() {
        String[] names = getIntent().getStringArrayExtra(EXTRA_PRESELECTED_NAMES);
        String[] numbers = getIntent().getStringArrayExtra(EXTRA_PRESELECTED_NUMBERS);
        long[] ids = getIntent().getLongArrayExtra(EXTRA_PRESELECTED_CONTACT_IDS);
        if (numbers == null) return;
        for (int i = 0; i < numbers.length; i++) {
            String number = numbers[i];
            if (number == null || number.trim().isEmpty()) continue;
            long contactId = ids != null && i < ids.length ? ids[i] : -(i + 1L);
            String name = names != null && i < names.length ? names[i] : number;
            selected.put(contactId, new SelectedRecipient(contactId, name, number));
        }
    }

    private void updateSelectionUi() {
        if (selectedCount != null) {
            selectedCount.setText(getString(R.string.recipients_selected, selected.size()));
        }
        if (done != null) {
            done.setAlpha(selected.isEmpty() ? 0.45f : 1f);
            done.setEnabled(!selected.isEmpty());
        }
    }

    private void finishSelection() {
        if (selected.isEmpty()) return;
        String[] names = new String[selected.size()];
        String[] numbers = new String[selected.size()];
        long[] ids = new long[selected.size()];
        int i = 0;
        for (SelectedRecipient recipient : selected.values()) {
            names[i] = recipient.name;
            numbers[i] = recipient.number;
            ids[i] = recipient.contactId;
            i++;
        }
        Intent result = new Intent();
        result.putExtra(EXTRA_NAMES, names);
        result.putExtra(EXTRA_NUMBERS, numbers);
        result.putExtra(EXTRA_CONTACT_IDS, ids);
        if (numbers.length == 1) {
            result.putExtra(EXTRA_NAME, names[0]);
            result.putExtra(EXTRA_NUMBER, numbers[0]);
        }
        setResult(RESULT_OK, result);
        finish();
    }

    private void selectNumber(ContactEntry contact, String number) {
        SelectedRecipient existing = selected.get(contact.id);
        if (existing != null && PhoneNumberUtils.compare(existing.number, number)) {
            selected.remove(contact.id);
        } else {
            selected.put(contact.id, new SelectedRecipient(contact.id, contact.name, number));
        }
        updateSelectionUi();
        showContactList();
        adapter.notifyDataSetChanged();
    }

    private void showContactList() {
        content.removeAllViews();
        if (contactsList.getParent() != null) {
            ((ViewGroup) contactsList.getParent()).removeView(contactsList);
        }
        content.addView(contactsList, new LinearLayout.LayoutParams(-1, 0, 1f));
        if (empty.getParent() != null) {
            ((ViewGroup) empty.getParent()).removeView(empty);
        }
        content.addView(empty, new LinearLayout.LayoutParams(-1, Ui.dp(content, 64)));
        empty.setVisibility(shown.isEmpty() ? View.VISIBLE : View.GONE);
    }

    private void loadContacts() {
        io.execute(() -> {
            ArrayList<ContactEntry> loaded = new ArrayList<>();
            Set<Long> seen = new HashSet<>();
            try (Cursor c = getContentResolver().query(
                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    new String[]{
                            ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
                            ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY
                    },
                    null, null,
                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY + " COLLATE LOCALIZED ASC")) {
                if (c != null) {
                    while (c.moveToNext()) {
                        long id = c.getLong(0);
                        if (!seen.add(id)) continue;
                        String name = c.getString(1);
                        if (name == null || name.trim().isEmpty()) name = getString(R.string.unknown_contact);
                        loaded.add(new ContactEntry(id, name));
                    }
                }
            } catch (RuntimeException ignored) {}
            runOnUiThread(() -> {
                if (isFinishing() || isDestroyed()) return;
                all.clear();
                all.addAll(loaded);
                filter();
            });
        });
    }

    private void filter() {
        String q = query == null ? "" : query.trim().toLowerCase(Locale.getDefault());
        shown.clear();
        if (q.isEmpty()) {
            shown.addAll(all);
            adapter.notifyDataSetChanged();
            empty.setVisibility(shown.isEmpty() ? View.VISIBLE : View.GONE);
            return;
        }

        // Name filtering is immediate; number filtering is added asynchronously.
        for (ContactEntry e : all) {
            if (e.name.toLowerCase(Locale.getDefault()).contains(q)) shown.add(e);
        }
        adapter.notifyDataSetChanged();
        empty.setVisibility(shown.isEmpty() ? View.VISIBLE : View.GONE);

        final String expected = query;
        io.execute(() -> {
            Set<Long> numberMatches = new HashSet<>();
            String digits = q.replaceAll("[^0-9+]", "");
            if (!digits.isEmpty()) {
                try (Cursor c = getContentResolver().query(
                        ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        new String[]{
                                ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
                                ContactsContract.CommonDataKinds.Phone.NUMBER
                        }, null, null, null)) {
                    if (c != null) while (c.moveToNext()) {
                        String n = c.getString(1);
                        if (n != null && PhoneNumberUtils.normalizeNumber(n).contains(
                                PhoneNumberUtils.normalizeNumber(digits))) numberMatches.add(c.getLong(0));
                    }
                } catch (RuntimeException ignored) {}
            }
            runOnUiThread(() -> {
                if (isFinishing() || isDestroyed() || !expected.equals(query)) return;
                Set<Long> present = new HashSet<>();
                for (ContactEntry e : shown) present.add(e.id);
                for (ContactEntry e : all) {
                    if (numberMatches.contains(e.id) && present.add(e.id)) shown.add(e);
                }
                adapter.notifyDataSetChanged();
                empty.setVisibility(shown.isEmpty() ? View.VISIBLE : View.GONE);
            });
        });
    }

    private void chooseContact(ContactEntry contact) {
        io.execute(() -> {
            ArrayList<NumberEntry> numbers = new ArrayList<>();
            Set<String> normalizedSeen = new HashSet<>();
            try (Cursor c = getContentResolver().query(
                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    new String[]{
                            ContactsContract.CommonDataKinds.Phone.NUMBER,
                            ContactsContract.CommonDataKinds.Phone.TYPE,
                            ContactsContract.CommonDataKinds.Phone.LABEL
                    },
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
                    new String[]{Long.toString(contact.id)},
                    ContactsContract.CommonDataKinds.Phone.IS_PRIMARY + " DESC")) {
                if (c != null) {
                    while (c.moveToNext()) {
                        String raw = c.getString(0);
                        if (raw == null || raw.trim().isEmpty()) continue;
                        String normalized = PhoneNumberUtils.normalizeNumber(raw);
                        if (raw.trim().startsWith("+") && !normalized.startsWith("+")) {
                            normalized = "+" + normalized;
                        }
                        if (normalized.isEmpty() || !normalizedSeen.add(normalized)) continue;
                        int type = c.getInt(1);
                        String custom = c.getString(2);
                        CharSequence label = ContactsContract.CommonDataKinds.Phone.getTypeLabel(
                                getResources(), type, custom);
                        numbers.add(new NumberEntry(normalized, label == null ? "" : label.toString()));
                    }
                }
            } catch (RuntimeException ignored) {}

            runOnUiThread(() -> {
                if (isFinishing() || isDestroyed()) return;
                if (numbers.isEmpty()) return;
                if (numbers.size() == 1) {
                    selectNumber(contact, numbers.get(0).number);
                } else {
                    showNumbers(contact, numbers);
                }
            });
        });
    }

    private void showNumbers(ContactEntry contact, List<NumberEntry> numbers) {
        content.removeAllViews();

        TextView backToContacts = Ui.text(this, getString(R.string.back_to_contacts), 14, true);
        backToContacts.setTextColor(getColor(R.color.lemenzo_blue));
        backToContacts.setPadding(Ui.dp(backToContacts, 6), Ui.dp(backToContacts, 8),
                Ui.dp(backToContacts, 6), Ui.dp(backToContacts, 8));
        backToContacts.setOnClickListener(v -> showContactList());
        content.addView(backToContacts, new LinearLayout.LayoutParams(-1, -2));

        TextView name = Ui.text(this, contact.name, 20, true);
        name.setPadding(Ui.dp(name, 6), Ui.dp(name, 8), Ui.dp(name, 6), Ui.dp(name, 12));
        content.addView(name, new LinearLayout.LayoutParams(-1, -2));

        TextView hint = Ui.text(this, getString(R.string.choose_number), 14, false);
        hint.setTextColor(getColor(R.color.lemenzo_text_secondary));
        hint.setPadding(Ui.dp(hint, 6), 0, Ui.dp(hint, 6), Ui.dp(hint, 10));
        content.addView(hint, new LinearLayout.LayoutParams(-1, -2));

        for (NumberEntry n : numbers) {
            LinearLayout row = new LinearLayout(this);
            row.setOrientation(LinearLayout.VERTICAL);
            row.setGravity(Gravity.CENTER_VERTICAL);
            row.setBackgroundResource(R.drawable.card_background);
            row.setPadding(Ui.dp(row, 16), Ui.dp(row, 12), Ui.dp(row, 16), Ui.dp(row, 12));

            TextView label = Ui.text(this, n.label, 13, false);
            label.setTextColor(getColor(R.color.lemenzo_blue));
            row.addView(label, new LinearLayout.LayoutParams(-1, -2));

            TextView number = Ui.text(this, n.number, 17, false);
            row.addView(number, new LinearLayout.LayoutParams(-1, -2));

            row.setOnClickListener(v -> selectNumber(contact, n.number));
            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(-1, -2);
            lp.setMargins(0, 0, 0, Ui.dp(row, 8));
            content.addView(row, lp);
        }
    }

    private void finishWith(String name, String number) {
        Intent result = new Intent();
        result.putExtra(EXTRA_NAME, name);
        result.putExtra(EXTRA_NUMBER, number);
        setResult(RESULT_OK, result);
        finish();
    }

    private final class ContactAdapter extends BaseAdapter {
        @Override public int getCount() { return shown.size(); }
        @Override public Object getItem(int p) { return shown.get(p); }
        @Override public long getItemId(int p) { return shown.get(p).id; }

        @Override public View getView(int p, View convert, ViewGroup parent) {
            LinearLayout row;
            TextView avatar, name, check;
            if (convert == null) {
                row = new LinearLayout(ContactPickerActivity.this);
                row.setGravity(Gravity.CENTER_VERTICAL);
                row.setPadding(Ui.dp(row, 6), Ui.dp(row, 8), Ui.dp(row, 6), Ui.dp(row, 8));

                avatar = Ui.text(ContactPickerActivity.this, "", 15, true);
                avatar.setGravity(Gravity.CENTER);
                avatar.setTextColor(getColor(R.color.lemenzo_text_primary));
                avatar.setBackgroundResource(R.drawable.avatar_background);
                row.addView(avatar, new LinearLayout.LayoutParams(Ui.dp(row, 46), Ui.dp(row, 46)));

                name = Ui.text(ContactPickerActivity.this, "", 17, false);
                name.setPadding(Ui.dp(row, 14), 0, 0, 0);
                row.addView(name, new LinearLayout.LayoutParams(0, Ui.dp(row, 56), 1f));

                check = Ui.text(ContactPickerActivity.this, "", 15, true);
                check.setGravity(Gravity.CENTER);
                row.addView(check, new LinearLayout.LayoutParams(Ui.dp(row, 30), Ui.dp(row, 30)));

                row.setTag(new TextView[]{avatar, name, check});
            } else {
                row = (LinearLayout) convert;
                TextView[] tags = (TextView[]) row.getTag();
                avatar = tags[0];
                name = tags[1];
                check = tags[2];
            }

            ContactEntry e = shown.get(p);
            name.setText(e.name);
            boolean isSelected = selected.containsKey(e.id);
            check.setText(isSelected ? "✓" : "");
            check.setTextColor(getColor(R.color.lemenzo_text_primary));
            check.setBackgroundResource(isSelected
                    ? R.drawable.selection_selected
                    : R.drawable.selection_unselected);
            String trimmed = e.name.trim();
            avatar.setText(trimmed.isEmpty() ? "?" : trimmed.substring(0, 1).toUpperCase(Locale.getDefault()));
            return row;
        }
    }
}
