package com.lemenzo.multisms;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;

public final class ComposeActivity extends Activity {
    static final class SendTarget {
        final String number;
        final String text;
        SendTarget(String number, String text) {
            this.number = number;
            this.text = text;
        }
    }

    GroupStore store;
    long gid;
    List<GroupStore.Member> recipients;
    EditText body;
    TextView metrics;
    TextView segmentNotice;
    TextView status;
    Button send;
    Button retry;

    final LinkedHashMap<String,String> requests = new LinkedHashMap<>();
    BroadcastReceiver updates;
    final Handler mainHandler = new Handler(Looper.getMainLooper());
    final Runnable metricsTask = this::updateMetricsNow;
    final Runnable statusTask = this::refreshStatusNow;
    boolean dispatching = false;

    public void onCreate(Bundle b) {
        super.onCreate(b);
        store = new GroupStore(this);
        gid = getIntent().getLongExtra("group_id", 0);
        recipients = store.members(gid);
        loadRequests();
        render();

        updates = new BroadcastReceiver() {
            public void onReceive(Context c, Intent i) {
                scheduleStatusRefresh();
            }
        };
        registerReceiver(
                updates,
                new IntentFilter("com.lemenzo.multisms.STATUS_UPDATED"),
                Context.RECEIVER_NOT_EXPORTED);
    }

    protected void onDestroy() {
        mainHandler.removeCallbacks(metricsTask);
        mainHandler.removeCallbacks(statusTask);
        if (updates != null) unregisterReceiver(updates);
        super.onDestroy();
    }

    void render() {
        ScrollView sv = new ScrollView(this);
        sv.setFillViewport(true);
        LinearLayout root = Ui.root(this);
        sv.addView(root);

        root.addView(Ui.brandHeader(this));
        Ui.gap(this, root, 14);

        LinearLayout pageHeader = new LinearLayout(this);
        pageHeader.setGravity(Gravity.CENTER_VERTICAL);

        ImageButton back = Ui.iconButton(
                this, R.drawable.ic_back, getString(R.string.navigate_back));
        back.setOnClickListener(v -> finish());
        pageHeader.addView(back,
                new LinearLayout.LayoutParams(Ui.dp(this, 44), Ui.dp(this, 48)));

        LinearLayout titleBox = new LinearLayout(this);
        titleBox.setOrientation(LinearLayout.VERTICAL);
        titleBox.setPadding(Ui.dp(this, 7), 0, 0, 0);

        TextView title = Ui.text(this, store.groupName(gid), 24, true);
        titleBox.addView(title);

        TextView recipientCount = Ui.text(
                this, getString(R.string.recipients, recipients.size()), 13, false);
        recipientCount.setTextColor(getColor(R.color.lemenzo_text_secondary));
        titleBox.addView(recipientCount);

        pageHeader.addView(titleBox, new LinearLayout.LayoutParams(0, -2, 1f));
        root.addView(pageHeader);

        Ui.gap(this, root, 14);

        LinearLayout privacy = Ui.softCard(this);
        TextView privacyTitle = Ui.text(this, getString(R.string.private_delivery_title), 14, true);
        privacyTitle.setTextColor(getColor(R.color.lemenzo_blue));
        privacy.addView(privacyTitle);
        Ui.gap(this, privacy, 5);
        TextView privacyBody = Ui.text(this, getString(R.string.private_delivery_description), 13, false);
        privacyBody.setTextColor(getColor(R.color.lemenzo_text_secondary));
        privacyBody.setLineSpacing(0f, 1.08f);
        privacy.addView(privacyBody);
        root.addView(privacy);

        Ui.gap(this, root, 18);

        TextView messageLabel = Ui.text(this, getString(R.string.message), 15, true);
        root.addView(messageLabel);
        Ui.gap(this, root, 8);

        body = Ui.input(this, getString(R.string.write_message));
        body.setSingleLine(false);
        body.setGravity(Gravity.TOP);
        body.setMinHeight(Ui.dp(this, 168));
        body.setMaxLines(8);
        root.addView(body);

        Ui.gap(this, root, 12);

        LinearLayout usageCard = Ui.softCard(this);
        TextView usageTitle = Ui.text(this, getString(R.string.sms_usage_title), 15, true);
        usageCard.addView(usageTitle);
        Ui.gap(this, usageCard, 8);

        metrics = Ui.text(this, "", 14, true);
        metrics.setTextColor(getColor(R.color.lemenzo_text_primary));
        metrics.setLineSpacing(0f, 1.12f);
        usageCard.addView(metrics);

        Ui.gap(this, usageCard, 9);

        segmentNotice = Ui.text(this, "", 12, false);
        segmentNotice.setTextColor(getColor(R.color.lemenzo_info));
        segmentNotice.setLineSpacing(0f, 1.12f);
        usageCard.addView(segmentNotice);

        root.addView(usageCard);

        Ui.gap(this, root, 12);

        LinearLayout guidanceCard = Ui.softCard(this);
        TextView guidanceTitle = Ui.text(
                this, getString(R.string.daily_sms_guidance_title), 14, true);
        guidanceTitle.setTextColor(getColor(R.color.lemenzo_blue));
        guidanceCard.addView(guidanceTitle);
        Ui.gap(this, guidanceCard, 6);

        TextView dailyGuidance = Ui.text(
                this, getString(R.string.daily_sms_guidance), 12, false);
        dailyGuidance.setTextColor(getColor(R.color.lemenzo_text_secondary));
        dailyGuidance.setLineSpacing(0f, 1.1f);
        guidanceCard.addView(dailyGuidance);

        Ui.gap(this, guidanceCard, 8);

        TextView emojiRecommendation = Ui.text(
                this, getString(R.string.emoji_recommendation), 12, false);
        emojiRecommendation.setTextColor(getColor(R.color.lemenzo_info));
        emojiRecommendation.setLineSpacing(0f, 1.1f);
        guidanceCard.addView(emojiRecommendation);

        root.addView(guidanceCard);

        Ui.gap(this, root, 16);

        send = Ui.button(this, getString(R.string.send_to, recipients.size()));
        send.setOnClickListener(v -> confirm(false));
        root.addView(send);

        Ui.gap(this, root, 18);

        LinearLayout deliveryCard = Ui.softCard(this);
        TextView deliveryTitle = Ui.text(this, getString(R.string.delivery_status), 15, true);
        deliveryCard.addView(deliveryTitle);
        Ui.gap(this, deliveryCard, 7);

        status = Ui.text(this, getString(R.string.no_delivery_status), 13, false);
        status.setTextColor(getColor(R.color.lemenzo_text_secondary));
        status.setLineSpacing(0f, 1.12f);
        deliveryCard.addView(status);

        Ui.gap(this, deliveryCard, 10);
        retry = Ui.secondaryButton(this, getString(R.string.retry_failed));
        retry.setVisibility(android.view.View.GONE);
        retry.setOnClickListener(v -> confirm(true));
        deliveryCard.addView(retry);

        root.addView(deliveryCard);

        body.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) {
                scheduleMetrics();
            }
            public void afterTextChanged(Editable e) {}
        });

        setContentView(sv);
        updateMetrics();
        refreshStatusNow();
    }

    int segmentsFor(String text) {
        return SmsLengthCalculator.calculate(text).segments;
    }

    int totalSegments(boolean failedOnly) {
        SharedPreferences sp = getSharedPreferences("bulk_status", MODE_PRIVATE);
        int total = 0;
        String text = body.getText().toString();

        for (GroupStore.Member member : recipients) {
            if (failedOnly) {
                String old = requests.get(member.number);
                if (old == null || !"failed".equals(sp.getString(old, ""))) continue;
            }
            total += segmentsFor(text);
        }
        return total;
    }

    void scheduleMetrics() {
        mainHandler.removeCallbacks(metricsTask);
        mainHandler.postDelayed(metricsTask, 100);
    }

    void updateMetrics() {
        updateMetricsNow();
    }

    void updateMetricsNow() {
        String text = body.getText().toString();
        int recipientCount = recipients.size();

        if (text.isEmpty()) {
            metrics.setText(
                    getString(R.string.characters, 0)
                            + "\n"
                            + getString(R.string.sms_per_recipient, 0)
                            + "\n"
                            + getString(R.string.sms_total, 0));
            segmentNotice.setText(R.string.sms_length_explanation);
            return;
        }

        SmsLengthCalculator.Result result = SmsLengthCalculator.calculate(text);
        int total = result.segments * recipientCount;

        String encoding = result.isUnicode()
                ? getString(R.string.encoding_unicode)
                : getString(R.string.encoding_standard);

        metrics.setText(
                getString(R.string.characters, result.visibleCharacters)
                        + "   ·   " + encoding
                        + "\n"
                        + getString(R.string.sms_per_recipient, result.segments)
                        + "   ·   "
                        + getString(R.string.recipients, recipientCount)
                        + "\n"
                        + getString(R.string.sms_total, total)
                        + "\n"
                        + getString(R.string.sms_units_remaining, result.codeUnitsRemaining));

        if (result.isUnicode()) {
            segmentNotice.setText(
                    getString(
                            R.string.unicode_sms_explanation,
                            result.singleSegmentCapacity(),
                            result.concatenatedSegmentCapacity()));
        } else {
            segmentNotice.setText(
                    getString(
                            R.string.standard_sms_explanation,
                            result.singleSegmentCapacity(),
                            result.concatenatedSegmentCapacity()));
        }
    }

    void confirm(boolean failedOnly) {
        String text = body.getText().toString();
        if (text.trim().isEmpty() || recipients.isEmpty()) return;

        int count = failedOnly ? failedCount() : recipients.size();
        int total = totalSegments(failedOnly);
        if (count == 0 || total == 0) return;

        Ui.DialogParts dialog = Ui.showDialog(
                this,
                getString(R.string.confirm_send_title),
                getString(R.string.confirm_send_usage, count, total)
                        + "\n\n"
                        + getString(R.string.combined_message_notice),
                null,
                getString(R.string.cancel),
                getString(R.string.send_sms),
                false);

        dialog.positive.setOnClickListener(v -> {
            dialog.dialog.dismiss();
            dispatch(failedOnly);
        });
    }

    int failedCount() {
        int n = 0;
        SharedPreferences sp = getSharedPreferences("bulk_status", MODE_PRIVATE);
        for (String id : requests.values()) {
            if ("failed".equals(sp.getString(id, ""))) n++;
        }
        return n;
    }

    void dispatch(boolean failedOnly) {
        if (dispatching) return;

        SharedPreferences sp = getSharedPreferences("bulk_status", MODE_PRIVATE);
        ArrayList<SendTarget> targets = new ArrayList<>();
        String text = body.getText().toString();

        for (GroupStore.Member member : recipients) {
            String old = requests.get(member.number);
            if (failedOnly
                    && (old == null || !"failed".equals(sp.getString(old, "")))) {
                continue;
            }
            if (!text.trim().isEmpty()) {
                targets.add(new SendTarget(member.number, text));
            }
        }

        if (targets.isEmpty()) return;

        String broadcastKey = "broadcast." + gid;
        String broadcastMessageKey = "broadcast_message." + gid;

        // One Multi SMS group maps to one reusable local broadcast conversation
        // in Lemenzo Messages. A normal new send creates a new message entry inside
        // that conversation, while Retry Failed reuses the existing message entry.
        String broadcastId = sp.getString(broadcastKey, null);
        String broadcastMessageId =
                failedOnly ? sp.getString(broadcastMessageKey, null) : null;

        if (broadcastId == null || broadcastId.isEmpty()) {
            broadcastId = UUID.randomUUID().toString();
            sp.edit().putString(broadcastKey, broadcastId).apply();
        }

        if (broadcastMessageId == null || broadcastMessageId.isEmpty()) {
            broadcastMessageId = UUID.randomUUID().toString();
            sp.edit().putString(broadcastMessageKey, broadcastMessageId).apply();
        }

        dispatching = true;
        send.setEnabled(false);
        retry.setEnabled(false);
        body.setEnabled(false);

        dispatchNext(
                targets,
                0,
                broadcastId,
                broadcastMessageId);
    }

    void dispatchNext(
            ArrayList<SendTarget> targets,
            int index,
            String broadcastId,
            String broadcastMessageId) {

        if (index >= targets.size()) {
            dispatching = false;
            if (!isFinishing() && !isDestroyed()) {
                send.setEnabled(true);
                retry.setEnabled(true);
                body.setEnabled(true);
                scheduleStatusRefresh();
            }
            return;
        }

        SendTarget target = targets.get(index);
        String requestId = UUID.randomUUID().toString();
        SharedPreferences sp = getSharedPreferences("bulk_status", MODE_PRIVATE);

        String key = "request." + gid + "." + target.number;
        String previous = sp.getString(key, null);

        requests.put(target.number, requestId);

        SharedPreferences.Editor editor = sp.edit()
                .putString(key, requestId)
                .putString(requestId, "queued");

        if (previous != null && !previous.equals(requestId)) {
            editor.remove(previous);
        }
        editor.apply();

        String recipientName = target.number;
        for (GroupStore.Member member : recipients) {
            if (target.number.equals(member.number)) {
                recipientName =
                        member.name == null || member.name.trim().isEmpty()
                                ? member.number
                                : member.name.trim();
                break;
            }
        }

        Intent intent = new Intent("com.lemenzo.messenger.action.BULK_SEND")
                .setComponent(new ComponentName(
                        "com.lemenzo.messenger",
                        "com.lemenzo.messenger.sms.BulkSmsBridgeReceiver"))
                .putExtra("request_id", requestId)
                .putExtra("broadcast_id", broadcastId)
                .putExtra("broadcast_message_id", broadcastMessageId)
                .putExtra("broadcast_total", recipients.size())
                .putExtra("broadcast_title", store.groupName(gid))
                .putExtra("broadcast_body", body.getText().toString())
                .putExtra("recipient_name", recipientName)
                .putExtra("destination", target.number)
                .putExtra("text", target.text);

        try {
            sendBroadcast(intent, "com.lemenzo.permission.MULTI_SMS");
        } catch (RuntimeException ex) {
            sp.edit().putString(requestId, "failed").apply();
            if (!isFinishing() && !isDestroyed()) {
                Toast.makeText(
                        this,
                        R.string.messenger_unavailable,
                        Toast.LENGTH_LONG).show();
            }
        }

        if (!isFinishing() && !isDestroyed()) {
            scheduleStatusRefresh();
        }

        mainHandler.postDelayed(
                () -> dispatchNext(
                        targets,
                        index + 1,
                        broadcastId,
                        broadcastMessageId),
                25);
    }

    void scheduleStatusRefresh() {
        mainHandler.removeCallbacks(statusTask);
        mainHandler.postDelayed(statusTask, 100);
    }

    void refreshStatus() {
        scheduleStatusRefresh();
    }

    void refreshStatusNow() {
        if (status == null) return;

        SharedPreferences sp = getSharedPreferences("bulk_status", MODE_PRIVATE);
        int sent = 0;
        int failed = 0;
        int queued = 0;
        StringBuilder details = new StringBuilder();

        for (GroupStore.Member member : recipients) {
            String requestId = requests.get(member.number);
            if (requestId == null) continue;

            String state = sp.getString(requestId, "queued");
            if ("sent".equals(state)) sent++;
            else if ("failed".equals(state)) failed++;
            else queued++;

            details.append(
                    member.name == null || member.name.isEmpty()
                            ? member.number
                            : member.name);
            details.append("   ").append(localStatus(state)).append('\n');
        }

        if (sent == 0 && failed == 0 && queued == 0) {
            status.setText(R.string.no_delivery_status);
            status.setTextColor(getColor(R.color.lemenzo_text_secondary));
        } else {
            status.setText(
                    getString(R.string.delivery_summary, sent, failed, queued)
                            + "\n\n"
                            + details.toString().trim());
            status.setTextColor(
                    getColor(failed > 0
                            ? R.color.lemenzo_danger
                            : R.color.lemenzo_text_secondary));
        }

        retry.setVisibility(failed > 0
                ? android.view.View.VISIBLE
                : android.view.View.GONE);
    }

    void loadRequests() {
        SharedPreferences prefs =
                getSharedPreferences("bulk_status", MODE_PRIVATE);

        for (GroupStore.Member member : recipients) {
            String requestId =
                    prefs.getString("request." + gid + "." + member.number, null);
            if (requestId != null) {
                requests.put(member.number, requestId);
            }
        }
    }

    String localStatus(String state) {
        if ("sent".equals(state)) return getString(R.string.sent);
        if ("failed".equals(state)) return getString(R.string.failed);
        return getString(R.string.queued);
    }
}
