package com.bgycc.smartcanteen.viewModel;

import android.text.TextUtils;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;

import com.bgycc.smartcanteen.command.CommandProgressCallback;
import com.bgycc.smartcanteen.command.CommandHandler;
import com.bgycc.smartcanteen.command.LogCommandHandler;
import com.bgycc.smartcanteen.command.UpdateCommandHandler;
import com.bgycc.smartcanteen.command.WifiConfigCommandHandler;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandResponse;
import com.bgycc.smartcanteen.executor.SCTaskExecutor;
import com.bgycc.smartcanteen.socket.SCWebSocketClient;
import com.bgycc.smartcanteen.socket.SCWebSocketListener;
import com.bgycc.smartcanteen.socket.SCWebSocketListenerAdapter;
import com.bgycc.smartcanteen.state.CommandState;
import com.bgycc.smartcanteen.repository.CommandRepository;
import com.blankj.utilcode.util.LogUtils;
import com.google.gson.Gson;
import com.google.gson.JsonObject;

import java.util.List;

import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;

/**
 * 监听本地扫码、服务器下发的设置指令 <br/>
 * 设备指令需要匹配以下规则:  <br/>
 * 1、action非"PAY_RESULT"  <br/><br/>
 * 监听Command数据库的变动,按如下流程进行处理: <br/>
 * 从数据库读取 -> 解析执行 -> 更新到数据库
 */
public class CommandViewModel extends ViewModel implements CommandProgressCallback {
    private CommandRepository commandRepository;
    private Gson gson;
    private String deviceSN;

    private MutableLiveData<CommandState> commandState = new MutableLiveData<>();
    private LiveData<List<Command>> dataLiveData;

    public LiveData<CommandState> getCommandStateEvent() {
        return commandState;
    }

    public CommandViewModel(CommandRepository commandRepository, Gson gson, String deviceSN) {
        this.commandRepository = commandRepository;
        this.gson = gson;
        this.deviceSN = deviceSN;
        // 监听数据库的变动,并执行未完成的指令
        this.dataLiveData = commandRepository.queryUndoneCommand();
        this.dataLiveData.observeForever(dataObserver);
        this.commandState.postValue(new CommandState(CommandState.IDLE));
    }

    public void initialize() {
        SCWebSocketClient.getInstance().addListener(listener);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        if (dataLiveData != null) {
            dataLiveData.removeObserver(dataObserver);
        }
    }

    private Observer<List<Command>> dataObserver = commands -> {
        if (commands == null || commands.isEmpty()) {
            commandState.postValue(new CommandState(CommandState.IDLE));
            return;
        }
        Command first = commands.get(0);
        RequestRunnable runnable = new RequestRunnable(first);
        SCTaskExecutor.getInstance().executeOnDiskIO(runnable);
    };

    @Override
    public void progress(String message, int progress) {
        commandState.postValue(new CommandState(CommandState.WAIT, message, progress));
    }

    private SCWebSocketListener listener = new SCWebSocketListenerAdapter() {
        private static final String RESPONSE_PAY_RESULT = "PAY_RESULT";

        @Override
        public void onMessage(String action, JsonObject obj, String original) {
            // 设备指令需要匹配以下规则:
            // 1、action非"PAY_RESULT"
            if (TextUtils.isEmpty(action) || action.equals(RESPONSE_PAY_RESULT)) return;
            LogUtils.d(TAG, "设备下发指令: " + original);
            ResponseRunnable runnable = new ResponseRunnable(original, action);
            SCTaskExecutor.getInstance().executeOnDiskIO(runnable);
        }
    };

    private class RequestRunnable implements Runnable {
        // 每条指令执行间隔
        private static final long EXEC_INTERVAL = 1500;
        private Command command;

        RequestRunnable(Command command) {
            this.command = command;
        }

        @Override
        public void run() {
            CommandHandler handler = null;
            try {
                switch (command.getAction()) {
                    case Command.LOG_UPLOAD:
                        handler = new LogCommandHandler(command, gson, deviceSN,CommandViewModel.this);
                        break;
                    case Command.APP_UPDATE:
                        handler = new UpdateCommandHandler(command, gson, CommandViewModel.this);
                        break;
                    case Command.CONFIG_WIFI:
                        handler = new WifiConfigCommandHandler(command, gson, CommandViewModel.this);
                        break;
                }
            } catch (Exception e) {
                commandState.postValue(new CommandState(CommandState.FAILED, e.getMessage()));
                handler = null;
            }
            if (handler == null) {
                LogUtils.w(TAG, "无法识别指令: " + command.toString());
                commandFinishAndUpdateDB();
                return;
            }
            try {
                commandState.postValue(new CommandState(CommandState.WAIT));
                CommandResponse response = handler.run();
                if (response.success()) {
                    commandState.postValue(new CommandState(CommandState.SUCCESS, response.getMessage()));
                } else {
                    commandState.postValue(new CommandState(CommandState.FAILED, response.getMessage()));
                }
            } catch (Exception e) {
                commandState.postValue(new CommandState(CommandState.FAILED, e.getMessage()));
            } finally {
                commandFinishAndUpdateDB();
            }
        }

        // 将执行完毕的指令更新到数据库,此时会触发dataObserver的动作(会继续搜索下一条未执行完毕的指令)
        private void commandFinishAndUpdateDB() {
            try {
                Thread.sleep(EXEC_INTERVAL);
            } catch (InterruptedException e) {
                LogUtils.w(TAG, e.getMessage(), e);
            } finally {
                command.finish();
                commandRepository.updateCommand(command);
            }
        }
    }

    private class ResponseRunnable implements Runnable {
        private String response;
        private String action;

        ResponseRunnable(String response, String action) {
            this.response = response;
            this.action = action;
        }

        @Override
        public void run() {
            // 指令插入到数据库,则会触发dataObserver
            Command command = new Command(response, action);
            long lastInsertId = commandRepository.insertCommand(command);
            if (lastInsertId == -1) {
                LogUtils.w(TAG, "指令插入到数据库失败: " + command.toString());
            }
        }
    }
}