package com.ttstd.controlled.network;

import android.content.Context;
import android.os.Build;
import android.text.TextUtils;

import com.google.gson.JsonObject;
import com.ttstd.controlled.BuildConfig;
import com.ttstd.controlled.network.model.PairingCodeResponse;
import com.ttstd.controlled.network.model.ProvisionRequest;
import com.ttstd.controlled.network.model.ProvisionResponse;
import com.ttstd.controlled.network.model.TokenRequest;
import com.ttstd.controlled.network.model.TokenResponse;
import com.ttstd.controlled.utils.DeviceSecretStore;
import com.ttstd.controlled.utils.DeviceUtils;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;

/**
 * 设备激活 / 令牌仓库。
 *
 * 统一封装 provision + token 两步流程与凭据落盘，所有方法返回冷 Single，
 * 订阅时才发起请求，交由调用方（ViewModel）用 CompositeDisposable 管理生命周期。
 */
public class DeviceRepository {

    /** 出厂预置共享密钥，由 build.gradle 的 buildConfigField 注入。 */
    private static final String PROVISION_SECRET = BuildConfig.DEVICE_PROVISION_SECRET;

    private final DeviceApi api;
    private final DeviceSecretStore store;
    private final Context appContext;

    public DeviceRepository(Context context) {
        this.appContext = context.getApplicationContext();
        this.api = RetrofitClient.deviceApi();
        this.store = new DeviceSecretStore(this.appContext);
    }

    public DeviceSecretStore store() {
        return store;
    }

    public boolean isActivated() {
        return store.isActivated();
    }

    public String getDeviceUid() {
        return store.getDeviceUid();
    }

    public String getAccessToken() {
        return store.getAccessToken();
    }

    /**
     * 完整激活流程：provision 拿到 deviceUid/deviceSecret 并落盘，随后换取 accessToken。
     *
     * 若设备已激活则跳过 provision，直接用已有凭据换 token（即「刷新」语义）。
     *
     * @return 发射最终可用的 accessToken
     */
    public Single<String> activate() {
        return Single.defer(() -> {
            if (store.isActivated()) {
                return exchangeToken(store.getDeviceUid(), store.getDeviceSecret());
            }
            return provision().flatMap(resp -> exchangeToken(resp.getDeviceUid(), resp.getDeviceSecret()));
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    /** 强制重新激活：清空本地凭据后走完整 provision 流程。用于激活页「刷新重试」。 */
    public Single<String> reactivate() {
        return Single.defer(() -> {
            store.clear();
            return provision().flatMap(resp -> exchangeToken(resp.getDeviceUid(), resp.getDeviceSecret()));
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    /** 第一步：用 SN + HMAC 证明出厂身份，成功后立即加密落盘。 */
    private Single<ProvisionResponse> provision() {
        return Single.fromCallable(() -> {
            String sn = DeviceUtils.getStableId(appContext);
            if (TextUtils.isEmpty(sn)) {
                throw new IllegalStateException("无法获取设备序列号，无法激活");
            }
            long timestamp = System.currentTimeMillis() / 1000L;
            String nonce = Long.toHexString(System.nanoTime()) + Long.toHexString(System.currentTimeMillis());
            String hmac = HmacSigner.signProvision(PROVISION_SECRET, sn, nonce, timestamp);
            return new ProvisionRequest(sn, Build.MODEL, nonce, timestamp, hmac);
        }).flatMap(api::provision).map(resp -> {
            if (resp == null || !resp.isValid()) {
                throw new IllegalStateException("激活失败：服务端未返回有效的设备凭据");
            }
            store.saveDevice(resp.getDeviceUid(), resp.getDeviceSecret());
            return resp;
        });
    }

    /** 第二步：用 deviceUid + deviceSecret 换取 accessToken 并落盘。 */
    private Single<String> exchangeToken(String deviceUid, String deviceSecret) {
        if (TextUtils.isEmpty(deviceUid) || TextUtils.isEmpty(deviceSecret)) {
            return Single.error(new IllegalStateException("设备凭据缺失，请重新激活"));
        }
        return api.token(new TokenRequest(deviceUid, deviceSecret)).map(resp -> {
            if (resp == null || !resp.isValid()) {
                throw new IllegalStateException("令牌换取失败：服务端未返回 accessToken");
            }
            store.saveAccessToken(resp.getAccessToken());
            return resp.getAccessToken();
        });
    }

    /**
     * 拉取 TURN 凭证。服务端未开启或失败时返回 null（降级为仅 STUN），不中断主流程。
     */
    public Single<JsonObject> fetchTurnCredentials(String accessToken) {
        if (TextUtils.isEmpty(accessToken)) {
            return Single.just(new JsonObject());
        }
        return api.turnCredentials("Bearer " + accessToken)
                .onErrorReturnItem(new JsonObject())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    /**
     * 生成一次性配对码，供其他用户（控制端）兑换绑定。
     * 需设备已激活且持有有效的 accessToken。
     */
    public Single<PairingCodeResponse> generatePairingCode() {
        String token = getAccessToken();
        if (TextUtils.isEmpty(token)) {
            return Single.error(new IllegalStateException("设备尚未激活，无法生成配对码"));
        }
        return api.pairingCode("Bearer " + token)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }
}
