package com.nadi.player.nativeplayer.channels;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Process-wide singleton holding the channel list that JS pushed in.
 *
 * Why a static holder and not Intent extras?
 *   A 5,000-channel Xtream playlist serialised as String[][] easily blows
 *   past Android's ~1MB Binder limit when launching the PlayerActivity.
 *   The plugin writes once on play(); the activity reads on create.
 *   Both run in the same process, so this is just a fast in-memory hand-off.
 *
 * Thread-safety: writes happen on the Capacitor plugin thread, reads on
 * the Activity main thread. We expose immutable snapshots so callers can
 * iterate without locking. {@code volatile} on the list reference is
 * enough for the publish-once / read-many pattern we have.
 */
public final class ChannelStore {

    private static final ChannelStore INSTANCE = new ChannelStore();
    public static ChannelStore get() { return INSTANCE; }

    private volatile List<Channel> channels = Collections.emptyList();
    private volatile List<Category> categories = Collections.emptyList();
    /** Channel number currently playing — kept here so a relaunched
     *  PlayerActivity (after PiP, lock screen, etc.) can restore the
     *  "now playing" highlight without going back through JS. */
    private volatile int currentNumber = -1;

    private ChannelStore() {}

    /** Bulk-load from a JSON array. Categories are rebuilt automatically
     *  in first-seen order, so JS controls the displayed order. */
    public void setFromJson(JSONArray arr) {
        if (arr == null || arr.length() == 0) {
            channels   = Collections.emptyList();
            categories = Collections.emptyList();
            return;
        }
        List<Channel> next = new ArrayList<>(arr.length());
        for (int i = 0; i < arr.length(); i++) {
            try {
                JSONObject o = arr.getJSONObject(i);
                Channel c = Channel.fromJson(o, /* fallback # = */ i + 1);
                if (c != null) next.add(c);
            } catch (JSONException ignored) {
                /* skip malformed entries — keep going so one bad row
                   never kills the whole playlist. */
            }
        }
        channels = Collections.unmodifiableList(next);
        categories = buildCategories(next);
    }

    public List<Channel> channels() { return channels; }
    public List<Category> categories() { return categories; }
    public boolean isEmpty() { return channels.isEmpty(); }

    public int currentChannelNumber() { return currentNumber; }
    public void setCurrentChannelNumber(int n) { currentNumber = n; }

    /**
     * Fast-zap helper used by both the right-side overlay (D-pad UP/DOWN)
     * and by {@code ChannelAwarePlayer} (lock-screen / notification /
     * Bluetooth ⏮ / ⏭ / Assistant / home-screen widget).
     *
     * Walks the full catalogue (NOT the filtered list) so lock-screen
     * controls behave predictably regardless of which category the user
     * last looked at in the overlay. Returns null when the catalogue is
     * empty.
     */
    public Channel zapNext() { return zap(+1); }
    public Channel zapPrevious() { return zap(-1); }

    private Channel zap(int delta) {
        List<Channel> list = channels;          // snapshot for thread safety
        int count = list.size();
        if (count == 0) return null;
        int cur = -1;
        for (int i = 0; i < count; i++) {
            if (list.get(i).number == currentNumber) { cur = i; break; }
        }
        int next;
        if (cur < 0) {
            next = (delta > 0) ? 0 : count - 1;
        } else {
            next = (cur + delta + count) % count;
        }
        Channel ch = list.get(next);
        currentNumber = ch.number;
        return ch;
    }

    /** Static + dynamic categories.
     *  Static (always shown, even on an empty list): All, Favorites, Recent.
     *  Dynamic: every distinct {@code category} value, in first-seen order. */
    private static List<Category> buildCategories(List<Channel> list) {
        List<Category> out = new ArrayList<>(8);
        out.add(new Category(Category.KEY_ALL,       "All"));
        out.add(new Category(Category.KEY_FAVORITES, "Favorites"));
        out.add(new Category(Category.KEY_RECENT,    "Recent"));
        // LinkedHashMap keeps insertion order = the order JS sent us.
        Map<String, Boolean> seen = new LinkedHashMap<>();
        for (Channel c : list) {
            if (c.category == null || c.category.isEmpty()) continue;
            if (!seen.containsKey(c.category)) {
                seen.put(c.category, true);
                out.add(new Category(c.category, c.category));
            }
        }
        return Collections.unmodifiableList(out);
    }

    public static final class Category {
        public static final String KEY_ALL       = "__all__";
        public static final String KEY_FAVORITES = "__favorites__";
        public static final String KEY_RECENT    = "__recent__";

        public final String key;
        public final String label;
        public Category(String key, String label) {
            this.key = key;
            this.label = label;
        }
    }
}
