package com.foxx.androidcast.entitlements;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.foxx.androidcast.BuildConfig;

import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Process-wide paid-feature bitmask: loads native lib on demand, reads local obfuscated file, refreshes from BE.
 */
public final class EntitlementCoordinator {
    private static final String TAG = "EntitlementCoord";
    private static final long DEV_ACCOUNT_KEY = 0x0123456789ABCDEFL;

    private static volatile EntitlementCoordinator instance;

    private final Context appContext;
    private final ExecutorService syncExecutor = Executors.newSingleThreadExecutor(r -> {
        Thread t = new Thread(r, "entitlement-sync");
        t.setDaemon(true);
        return t;
    });
    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    private final AtomicLong flags = new AtomicLong(0L);

    private volatile boolean nativeCapable;

    public static EntitlementCoordinator getInstance(Context context) {
        if (instance == null) {
            synchronized (EntitlementCoordinator.class) {
                if (instance == null) {
                    instance = new EntitlementCoordinator(context.getApplicationContext());
                }
            }
        }
        return instance;
    }

    private EntitlementCoordinator(Context appContext) {
        this.appContext = appContext;
    }

    private volatile boolean bootstrapDone;

    /** Call once from {@link com.foxx.androidcast.AndroidCastApplication}. */
    public void bootstrap() {
        if (bootstrapDone) {
            return;
        }
        bootstrapDone = true;
        reloadFromLocal();
        scheduleBackendSync();
    }

    public long getFlags() {
        return flags.get();
    }

    public boolean hasFeature(long featureBit) {
        return EntitlementFlags.hasFeature(flags.get(), featureBit);
    }

    public boolean hasAnyPaidFeature() {
        return EntitlementFlags.hasAnyPaidBit(flags.get());
    }

    public boolean isNativeLibraryAvailable() {
        return nativeCapable;
    }

    /** Re-read local file after manual refresh or sync completion. */
    public void reloadFromLocal() {
        if (isDevUnlockAll()) {
            flags.set(EntitlementFlags.DEV_ALL_PAYLOAD);
            EntitlementNativeBridge.acquire();
            nativeCapable = EntitlementNativeBridge.isAvailable();
            if (nativeCapable) {
                try {
                    writeDevBlobIfNeeded();
                } finally {
                    EntitlementNativeBridge.release();
                }
            } else {
                EntitlementNativeBridge.release();
            }
            return;
        }

        EntitlementNativeBridge.acquire();
        nativeCapable = EntitlementNativeBridge.isAvailable();
        if (!nativeCapable) {
            EntitlementNativeBridge.release();
            flags.set(0L);
            return;
        }

        try {
            long now = System.currentTimeMillis();
            if (EntitlementPolicy.isClockRollback(now, EntitlementStore.getLastVerifiedWallMs(appContext),
                    EntitlementPolicy.CLOCK_ROLLBACK_TOLERANCE_MS)) {
                Log.w(TAG, "wall clock rollback detected — clearing entitlements");
                resetToDefaults();
                return;
            }

            File file = EntitlementStore.entitlementFile(appContext);
            if (!file.isFile()) {
                flags.set(0L);
                return;
            }
            if (EntitlementPolicy.isFileStale(file.lastModified(), now, EntitlementPolicy.FILE_TTL_MS)) {
                Log.i(TAG, "local entitlement file expired");
                resetToDefaults();
                return;
            }

            long key = EntitlementStore.getAccountKey(appContext);
            if (key == 0L) {
                flags.set(0L);
                return;
            }

            long[] out = new long[1];
            int rv = EntitlementNativeBridge.readFrom(file.getAbsolutePath(), key, out);
            if (rv != 0) {
                Log.w(TAG, "readFrom failed: " + rv);
                flags.set(0L);
                return;
            }
            flags.set(out[0]);
            EntitlementStore.setLastVerifiedWallMs(appContext, now);
        } finally {
            EntitlementNativeBridge.release();
        }
    }

    public void scheduleBackendSync() {
        syncExecutor.execute(this::runBackendSync);
    }

    private void runBackendSync() {
        EntitlementSyncClient.SyncPayload payload = EntitlementSyncClient.fetch(appContext);
        if (payload == null) {
            return;
        }
        if (!EntitlementSyncClient.persistPayload(appContext, payload)) {
            return;
        }
        mainHandler.post(this::reloadFromLocal);
    }

    private void resetToDefaults() {
        EntitlementStore.clearLocal(appContext);
        flags.set(0L);
    }

    private boolean isDevUnlockAll() {
        return BuildConfig.DEBUG;
    }

    /** Debug: materialize an all-features blob so Linux ↔ device file tests work without BE. */
    private void writeDevBlobIfNeeded() {
        File file = EntitlementStore.entitlementFile(appContext);
        if (file.isFile() && EntitlementStore.getAccountKey(appContext) == DEV_ACCOUNT_KEY) {
            return;
        }
        EntitlementStore.setAccountKey(appContext, DEV_ACCOUNT_KEY);
        int w = EntitlementNativeBridge.writeTo(file.getAbsolutePath(),
                EntitlementFlags.DEV_ALL_PAYLOAD, DEV_ACCOUNT_KEY);
        if (w != 0) {
            Log.w(TAG, "dev writeTo failed: " + w);
        }
    }
}
