package com.ttstd.controller.activity.main;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import com.ttstd.controller.base.mvvm.BaseViewModel;
import com.ttstd.controller.network.AuthRepository;
import com.ttstd.controller.network.model.BindingItem;
import com.ttstd.controller.network.model.OnlineDevicesResponse;
import com.ttstd.controller.network.model.VerifyResponse;
import com.ttstd.controller.utils.TokenStore;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 主页 ViewModel：负责令牌保活与绑定设备列表拉取。
 *
 * <p>所有网络请求均走 Retrofit + RxJava3，由基类 CompositeDisposable 托管，
 * Activity 销毁时自动取消，替代原先的 {@code new Thread} + {@code runOnUiThread} 写法。
 */
public class MainViewModel extends BaseViewModel {

    private final AuthRepository repository = new AuthRepository();

    /** 刷新单飞标记，避免并发重复刷新令牌。 */
    private final AtomicBoolean refreshInFlight = new AtomicBoolean(false);

    /** 可用的 accessToken，用于建立 WebSocket。 */
    private final MutableLiveData<String> accessToken = new MutableLiveData<>();

    /** 令牌重新可用（4001 过期后刷新成功），需要重连信令。 */
    private final MutableLiveData<String> reconnectToken = new MutableLiveData<>();

    /** 认证失败，需要重新登录。 */
    private final MutableLiveData<String> authFailed = new MutableLiveData<>();

    /** 已绑定设备列表。 */
    private final MutableLiveData<List<BindingItem>> bindings = new MutableLiveData<>();

    /** 当前已绑定且在线（可直接连接）的设备列表。 */
    private final MutableLiveData<List<BindingItem>> onlineDevices = new MutableLiveData<>();

    /** 设备列表加载失败提示。 */
    private final MutableLiveData<String> deviceError = new MutableLiveData<>();

    /** 兑换配对码成功后的绑定结果（用于界面反馈）。 */
    private final MutableLiveData<BindingItem> redeemResult = new MutableLiveData<>();

    /** 兑换配对码失败提示。 */
    private final MutableLiveData<String> redeemError = new MutableLiveData<>();

    /** 本机账号信息（用户 id / 用户名），对应 {@code GET /api/client/verify}。 */
    private final MutableLiveData<VerifyResponse> accountInfo = new MutableLiveData<>();

    /** 本机信息加载失败提示。 */
    private final MutableLiveData<String> accountError = new MutableLiveData<>();

    /** 列表刷新中标记，供 UI 控制刷新按钮可用性。 */
    private final MutableLiveData<Boolean> refreshing = new MutableLiveData<>(false);

    public LiveData<String> getAccessToken() {
        return accessToken;
    }

    public LiveData<String> getReconnectToken() {
        return reconnectToken;
    }

    public LiveData<String> getAuthFailed() {
        return authFailed;
    }

    public LiveData<List<BindingItem>> getBindings() {
        return bindings;
    }

    public LiveData<List<BindingItem>> getOnlineDevices() {
        return onlineDevices;
    }

    public LiveData<String> getDeviceError() {
        return deviceError;
    }

    public LiveData<BindingItem> getRedeemResult() {
        return redeemResult;
    }

    public LiveData<String> getRedeemError() {
        return redeemError;
    }

    public LiveData<VerifyResponse> getAccountInfo() {
        return accountInfo;
    }

    public LiveData<String> getAccountError() {
        return accountError;
    }

    public LiveData<Boolean> getRefreshing() {
        return refreshing;
    }

    /**
     * 确保 accessToken 有效：先校验，失效则自动刷新，结果通过
     * {@link #getAccessToken()} 回调到主线程。
     */
    public void prepareAccessToken() {
        execute(
                repository.checkLoginState(),
                valid -> {
                    if (Boolean.TRUE.equals(valid)) {
                        accessToken.setValue(TokenStore.get().getAccessToken());
                    } else {
                        authFailed.setValue("登录已失效，请重新登录");
                    }
                },
                error -> authFailed.setValue("认证失败：" + error.getMessage()));
    }

    /**
     * WebSocket 收到 4001（令牌过期）：单飞刷新后通知重连。
     */
    public void refreshTokenAndReconnect() {
        if (!refreshInFlight.compareAndSet(false, true)) {
            return;
        }
        execute(
                repository.refresh(),
                token -> {
                    refreshInFlight.set(false);
                    reconnectToken.setValue(token.getAccessToken());
                },
                error -> {
                    refreshInFlight.set(false);
                    authFailed.setValue("令牌刷新失败，请重新登录");
                });
    }

    /** 拉取当前用户已绑定的被控端设备列表。 */
    public void loadBindings() {
        execute(
                repository.bindings(),
                response -> bindings.setValue(response.getBindings()),
                error -> deviceError.setValue("绑定设备加载失败：" + error.getMessage()));
    }

    /** 拉取当前用户已绑定且在线（信令网络活跃）的被控端设备列表。 */
    public void loadOnlineDevices() {
        execute(
                repository.onlineDevices(),
                response -> onlineDevices.setValue(response.getDevices()),
                error -> deviceError.setValue("在线设备加载失败：" + error.getMessage()));
    }

    /** 拉取本机账号信息（用户 id / 用户名）。 */
    public void loadAccountInfo() {
        execute(
                repository.verify(),
                resp -> accountInfo.setValue(resp),
                error -> accountError.setValue("本机信息加载失败：" + error.getMessage()));
    }

    /**
     * 统一刷新：本机信息 + 已绑定设备 + 在线设备，供刷新按钮调用。
     * 通过 {@link #getRefreshing()} 暴露刷新中状态。
     */
    public void refreshAll() {
        refreshing.setValue(true);
        execute(
                repository.verify()
                        .doOnSuccess(accountInfo::setValue)
                        .flatMap(v -> repository.bindings())
                        .doOnSuccess(resp -> bindings.setValue(resp.getBindings()))
                        .flatMap(v -> repository.onlineDevices()),
                response -> {
                    onlineDevices.setValue(response.getDevices());
                    refreshing.setValue(false);
                },
                error -> {
                    deviceError.setValue("刷新失败：" + error.getMessage());
                    refreshing.setValue(false);
                });
    }

    /** 退出登录：清空本地令牌。 */
    public void logout() {
        repository.logout();
    }

    /** 兑换配对码，建立与被控端的绑定关系；成功后刷新设备列表。 */
    public void redeemPairingCode(String code) {
        execute(
                repository.redeemPairingCode(code),
                binding -> {
                    redeemResult.setValue(binding);
                    // 绑定成功后刷新可连设备列表
                    loadBindings();
                    loadOnlineDevices();
                },
                error -> redeemError.setValue("绑定失败：" + error.getMessage()));
    }
}
