package com.lemenzo.messenger.data;

import android.content.Context;
import android.content.SharedPreferences;
import com.lemenzo.messenger.model.BroadcastRecord;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

public final class BroadcastStore {
    public static final String PREFS = "lemenzo_private_broadcasts";
    private static final String KEY_INDEX = "index";
    private static final Object LOCK = new Object();
    private final SharedPreferences prefs;

    public BroadcastStore(Context context) {
        prefs = context.getApplicationContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
    }

    public SharedPreferences preferences() { return prefs; }

    public BroadcastRecord create(
            String body, List<BroadcastRecord.Recipient> recipients) {
        synchronized (LOCK) {
        BroadcastRecord r = new BroadcastRecord();
        r.id = UUID.randomUUID().toString();
        r.date = System.currentTimeMillis();
        r.title = "";
        if (recipients != null) r.recipients.addAll(recipients);
        r.expectedTotal = r.recipients.size();
        r.total = r.recipients.size();
        String messageId = UUID.randomUUID().toString();
        appendMessageInternal(r, messageId, body, r.recipients.size(), null);
        saveAndTouch(r);
        return r;
    
        }
    }

    public BroadcastRecord get(String id) {
        synchronized (LOCK) {
        if (id == null || id.isEmpty()) return null;
        String raw = prefs.getString("record." + id, null);
        if (raw == null) return null;
        try { return fromJson(new JSONObject(raw)); }
        catch (Exception ignored) { return null; }
    
        }
    }

    public List<BroadcastRecord> all() {
        synchronized (LOCK) {
        ArrayList<BroadcastRecord> out = new ArrayList<>();
        for (String id : readIndex()) {
            BroadcastRecord r = get(id);
            if (r != null) out.add(r);
        }
        Collections.sort(out, (a,b) -> Long.compare(b.date, a.date));
        return out;
    
        }
    }

    /** Starts a new outgoing message in an existing local broadcast conversation. */
    public String beginMessage(String broadcastId, String body) {
        synchronized (LOCK) {
        BroadcastRecord r = get(broadcastId);
        if (r == null || r.recipients.isEmpty() || body == null || body.trim().isEmpty()) return null;
        String messageId = UUID.randomUUID().toString();
        appendMessageInternal(r, messageId, body, r.recipients.size(), null);
        saveAndTouch(r);
        return messageId;
    
        }
    }

    /**
     * Multi SMS sends one receiver request at a time. This method merges those
     * requests into one local Messenger conversation and one message entry.
     */
    public BroadcastRecord upsertExternalRecipient(
            String id,
            String messageId,
            String title,
            String baseBody,
            int expectedTotal,
            long contactId,
            String name,
            String number,
            String renderedBody) {
        synchronized (LOCK) {
        if (id == null || id.isEmpty() || messageId == null || messageId.isEmpty()
                || number == null || number.isEmpty()) return null;

        BroadcastRecord r = get(id);
        if (r == null) {
            r = new BroadcastRecord();
            r.id = id;
            r.date = System.currentTimeMillis();
            r.title = title == null ? "" : title;
        }
        if (r.title == null || r.title.isEmpty()) r.title = title == null ? "" : title;
        if (expectedTotal > 0) r.expectedTotal = Math.max(r.expectedTotal, expectedTotal);

        BroadcastRecord.Recipient recipient = findRecipient(r, number);
        if (recipient == null) {
            recipient = new BroadcastRecord.Recipient(
                    contactId, name, number, "queued", renderedBody);
            r.recipients.add(recipient);
        } else {
            recipient.name = name == null || name.isEmpty() ? recipient.name : name;
            recipient.body = renderedBody == null ? "" : renderedBody;
            recipient.state = "queued";
        }

        BroadcastRecord.Message message = findMessage(r, messageId);
        if (message == null) {
            message = new BroadcastRecord.Message();
            message.id = messageId;
            message.date = System.currentTimeMillis();
            message.body = baseBody == null ? "" : baseBody;
            message.expectedTotal = expectedTotal > 0 ? expectedTotal : r.expectedTotal;
            r.messages.add(message);
        }
        if ((message.body == null || message.body.isEmpty()) && baseBody != null) message.body = baseBody;
        if (expectedTotal > 0) message.expectedTotal = Math.max(message.expectedTotal, expectedTotal);

        BroadcastRecord.MessageRecipient mr = findMessageRecipient(message, number);
        if (mr == null) {
            message.recipients.add(new BroadcastRecord.MessageRecipient(
                    number, name, "queued", renderedBody));
        } else {
            mr.name = name == null || name.isEmpty() ? mr.name : name;
            mr.renderedBody = renderedBody == null ? "" : renderedBody;
            mr.state = "queued";
        }

        recalcMessage(message);
        applyLatest(r, message);
        saveAndTouch(r);
        return r;
    
        }
    }

    /** Updates delivery state for a specific message/recipient pair. */
    public void updateRecipient(
            String id, String messageId, String number, String state) {
        synchronized (LOCK) {
        BroadcastRecord r = get(id);
        if (r == null || number == null) return;

        BroadcastRecord.Recipient recipient = findRecipient(r, number);
        if (recipient != null) recipient.state = state;

        BroadcastRecord.Message message = messageId == null ? r.latestMessage() : findMessage(r, messageId);
        if (message != null) {
            BroadcastRecord.MessageRecipient mr = findMessageRecipient(message, number);
            if (mr != null) mr.state = state;
            recalcMessage(message);
            if (message == r.latestMessage()) applyLatest(r, message);
        }
        saveAndTouch(r);
    
        }
    }

    /** Backward-compatible latest-message update. */
    public void updateRecipient(String id, String number, String state) {
        synchronized (LOCK) {
        BroadcastRecord r = get(id);
        updateRecipient(id, r == null || r.latestMessage() == null ? null : r.latestMessage().id,
                number, state);
    
        }
    }

    public void remove(String id) {
        synchronized (LOCK) {
        if (id == null) return;
        ArrayList<String> ids = readIndex();
        ids.remove(id);
        prefs.edit()
                .remove("record." + id)
                .putString(KEY_INDEX, new JSONArray(ids).toString())
                .commit();
    
        }
    }

    private static void appendMessageInternal(
            BroadcastRecord r,
            String messageId,
            String body,
            int expectedTotal,
            List<BroadcastRecord.MessageRecipient> renderedRecipients) {
        BroadcastRecord.Message m = new BroadcastRecord.Message();
        m.id = messageId;
        m.date = System.currentTimeMillis();
        m.body = body == null ? "" : body;
        m.expectedTotal = expectedTotal;
        if (renderedRecipients != null) {
            m.recipients.addAll(renderedRecipients);
        } else {
            for (BroadcastRecord.Recipient recipient : r.recipients) {
                recipient.state = "queued";
                recipient.body = m.body;
                m.recipients.add(new BroadcastRecord.MessageRecipient(
                        recipient.number, recipient.name, "queued", m.body));
            }
        }
        recalcMessage(m);
        r.messages.add(m);
        applyLatest(r, m);
    }

    private static BroadcastRecord.Recipient findRecipient(BroadcastRecord r, String number) {
        for (BroadcastRecord.Recipient x : r.recipients)
            if (number.equals(x.number)) return x;
        return null;
    }

    private static BroadcastRecord.Message findMessage(BroadcastRecord r, String id) {
        if (id == null) return null;
        for (BroadcastRecord.Message x : r.messages)
            if (id.equals(x.id)) return x;
        return null;
    }

    private static BroadcastRecord.MessageRecipient findMessageRecipient(
            BroadcastRecord.Message m, String number) {
        for (BroadcastRecord.MessageRecipient x : m.recipients)
            if (number.equals(x.number)) return x;
        return null;
    }

    private static void recalcMessage(BroadcastRecord.Message m) {
        int sent=0,failed=0;
        for(BroadcastRecord.MessageRecipient x:m.recipients){
            if("sent".equals(x.state))sent++;
            else if("failed".equals(x.state))failed++;
        }
        m.sent=sent;
        m.failed=failed;
        if(m.expectedTotal<=0)m.expectedTotal=m.recipients.size();
    }

    private static void applyLatest(BroadcastRecord r, BroadcastRecord.Message m) {
        r.body = m.body == null ? "" : m.body;
        r.date = m.date;
        r.sent = m.sent;
        r.failed = m.failed;
        r.total = Math.max(r.expectedTotal, r.recipients.size());
        if (r.expectedTotal <= 0) r.expectedTotal = r.total;
    }

    private void saveAndTouch(BroadcastRecord r) {
        saveRecord(r);
        ArrayList<String> ids = readIndex();
        ids.remove(r.id);
        ids.add(0,r.id);
        writeIndex(ids);
    }

    private void saveRecord(BroadcastRecord r) {
        prefs.edit().putString("record." + r.id, toJson(r).toString()).commit();
    }

    private ArrayList<String> readIndex() {
        ArrayList<String> out = new ArrayList<>();
        String raw = prefs.getString(KEY_INDEX, "[]");
        try {
            JSONArray a = new JSONArray(raw);
            for (int i=0;i<a.length();i++) {
                String id=a.optString(i,"");
                if(!id.isEmpty()) out.add(id);
            }
        } catch (Exception ignored) {}
        return out;
    }

    private void writeIndex(ArrayList<String> ids) {
        prefs.edit().putString(KEY_INDEX, new JSONArray(ids).toString()).commit();
    }

    private static JSONObject toJson(BroadcastRecord r) {
        JSONObject o = new JSONObject();
        try {
            o.put("id",r.id);
            o.put("date",r.date);
            o.put("body",r.body);
            o.put("title",r.title);
            o.put("expected_total",r.expectedTotal);

            JSONArray recipients = new JSONArray();
            for(BroadcastRecord.Recipient x:r.recipients){
                JSONObject j=new JSONObject();
                j.put("contact_id",x.contactId);
                j.put("name",x.name);
                j.put("number",x.number);
                j.put("state",x.state);
                j.put("body",x.body);
                recipients.put(j);
            }
            o.put("recipients",recipients);

            JSONArray messages = new JSONArray();
            for(BroadcastRecord.Message m:r.messages){
                JSONObject j=new JSONObject();
                j.put("id",m.id);
                j.put("date",m.date);
                j.put("body",m.body);
                j.put("expected_total",m.expectedTotal);
                JSONArray states=new JSONArray();
                for(BroadcastRecord.MessageRecipient x:m.recipients){
                    JSONObject y=new JSONObject();
                    y.put("number",x.number);
                    y.put("name",x.name);
                    y.put("state",x.state);
                    y.put("rendered_body",x.renderedBody);
                    states.put(y);
                }
                j.put("recipients",states);
                messages.put(j);
            }
            o.put("messages",messages);
        } catch(Exception ignored){}
        return o;
    }

    private static BroadcastRecord fromJson(JSONObject o) {
        BroadcastRecord r=new BroadcastRecord();
        r.id=o.optString("id","");
        r.date=o.optLong("date",0L);
        r.body=o.optString("body","");
        r.title=o.optString("title","");
        r.expectedTotal=o.optInt("expected_total",0);

        JSONArray recipients=o.optJSONArray("recipients");
        if(recipients!=null){
            for(int i=0;i<recipients.length();i++){
                JSONObject x=recipients.optJSONObject(i);
                if(x==null)continue;
                r.recipients.add(new BroadcastRecord.Recipient(
                        x.optLong("contact_id",-1L),
                        x.optString("name",""),
                        x.optString("number",""),
                        x.optString("state","queued"),
                        x.optString("body","")));
            }
        }

        JSONArray messages=o.optJSONArray("messages");
        if(messages!=null){
            for(int i=0;i<messages.length();i++){
                JSONObject x=messages.optJSONObject(i);
                if(x==null)continue;
                BroadcastRecord.Message m=new BroadcastRecord.Message();
                m.id=x.optString("id",UUID.randomUUID().toString());
                m.date=x.optLong("date",r.date);
                m.body=x.optString("body","");
                m.expectedTotal=x.optInt("expected_total",r.expectedTotal);
                JSONArray states=x.optJSONArray("recipients");
                if(states!=null){
                    for(int q=0;q<states.length();q++){
                        JSONObject y=states.optJSONObject(q);
                        if(y==null)continue;
                        m.recipients.add(new BroadcastRecord.MessageRecipient(
                                y.optString("number",""),
                                y.optString("name",""),
                                y.optString("state","queued"),
                                y.optString("rendered_body","")));
                    }
                }
                recalcMessage(m);
                r.messages.add(m);
            }
        }

        // v5.5 migration: a legacy one-message record had no "messages" array.
        if(r.messages.isEmpty() && r.body!=null && !r.body.isEmpty()){
            BroadcastRecord.Message legacy=new BroadcastRecord.Message();
            legacy.id=UUID.randomUUID().toString();
            legacy.date=r.date;
            legacy.body=r.body;
            legacy.expectedTotal=Math.max(r.expectedTotal,r.recipients.size());
            for(BroadcastRecord.Recipient x:r.recipients){
                legacy.recipients.add(new BroadcastRecord.MessageRecipient(
                        x.number,x.name,x.state,x.body==null||x.body.isEmpty()?r.body:x.body));
            }
            recalcMessage(legacy);
            r.messages.add(legacy);
        }

        BroadcastRecord.Message latest=r.latestMessage();
        if(latest!=null)applyLatest(r,latest);
        else{
            r.total=Math.max(r.expectedTotal,r.recipients.size());
        }
        return r;
    }
}
