package com.bgycc.smartcanteen;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.bgycc.smartcanteen.entity.PayData;
import com.bgycc.smartcanteen.executor.SCTaskExecutor;
import com.bgycc.smartcanteen.socket.SCWebSocketClient;
import com.bgycc.smartcanteen.state.CommandState;
import com.bgycc.smartcanteen.state.ConnectState;
import com.bgycc.smartcanteen.state.PayOfflineState;
import com.bgycc.smartcanteen.state.PayOnlineState;
import com.bgycc.smartcanteen.state.QRCodeState;
import com.bgycc.smartcanteen.utils.EthernetHelper;
import com.bgycc.smartcanteen.utils.NetworkHelper;
import com.bgycc.smartcanteen.utils.SmartCanteenUtils;
import com.bgycc.smartcanteen.utils.TTSHelper;
import com.bgycc.smartcanteen.utils.WifiHelper;
import com.bgycc.smartcanteen.viewModel.CommandViewModel;
import com.bgycc.smartcanteen.viewModel.PayOfflineViewModel;
import com.bgycc.smartcanteen.viewModel.PayOnlineViewModel;
import com.bgycc.smartcanteen.viewModel.QRCodeViewModel;
import com.bgycc.smartcanteen.viewModel.ViewModelFactory;
import com.blankj.utilcode.util.LogUtils;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

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

@SuppressWarnings("all")
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private PayOnlineViewModel payOnlineViewModel;
    private PayOfflineViewModel payOfflineViewModel;
    private QRCodeViewModel qrCodeViewModel;
    private CommandViewModel commandViewModel;

    private String deviceSN;
    private ViewStub debugVs;
    private LinearLayout debugLayout;
    private LinearLayout settingLayout;
    private ImageView logoLayout;
    private ImageView settingImg;
    private TextView versionText;
    private TextView snText;
    private TextView serverText;
    private TextView qrCodeText;
    private TextView sendText;
    private TextView recText;
    private TextView ethText;
    private TextView wifiText;
    private TextView settingText;
    private TextView socketConnectedTime;
    private TextView message;
    private AudioManager audioManager;

    private Handler handler = new Handler();
    private SimpleDateFormat payDateFormat = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault());
    private SimpleDateFormat socketConnectedTimeDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        deviceSN = SmartCanteenUtils.getDeviceSN(newBase.getApplicationContext());
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        NetworkHelper.initialize(newBase.getApplicationContext());
        TTSHelper.initialize(newBase.getApplicationContext());

        if (deviceSN.isEmpty()) {
            LogUtils.file(TAG, "设备SN号为空");
        }
        String host = SmartCanteenUtils.getMainWebSocketServerUrl(deviceSN, BuildConfig.VERSION_NAME);
        LogUtils.d(TAG, "服务器地址: " + host);
        try {
            URI uri = new URI(host);
            SCWebSocketClient.init(uri, deviceSN, Injection.provideGson());
        } catch (URISyntaxException e) {
            LogUtils.e(TAG, "服务器地址异常: " + e.getMessage(), e);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        ViewModelFactory factory = Injection.injectFactory(deviceSN);
        ViewModelProvider provider = new ViewModelProvider(this, factory);
        payOnlineViewModel = provider.get(PayOnlineViewModel.class);
        payOfflineViewModel = provider.get(PayOfflineViewModel.class);
        qrCodeViewModel = provider.get(QRCodeViewModel.class);
        commandViewModel = provider.get(CommandViewModel.class);

        initViews();

        SCWebSocketClient.getInstance().getConnectStateEvent().observe(this, event -> {
            if (debugVs != null) return;
            String networkType = NetworkHelper.getNetworkType(this);
            String msg;
            switch (event.getState()) {
                case ConnectState.OFFLINE:
                    msg = String.format(getString(R.string.connect_offline), networkType);
                    break;
                case ConnectState.CONNECTING:
                    msg = String.format(getString(R.string.connect_connecting), networkType);
                    break;
                case ConnectState.CONNECTED:
                    msg = String.format(getString(R.string.connect_connected), networkType);
                    // socket链接成功后刷新wifi及eth的ip地址信息
                    refreshEthInfo();
                    refreshWifiInfo();
                    break;
                case ConnectState.RECONNECTING:
                    msg = String.format(getString(R.string.connect_reconnecting), networkType);
                    break;
                case ConnectState.CHANGE_NETWORK:
                    msg = String.format(getString(R.string.connect_change_network), networkType);
                    break;
                default:
                    msg = "";
                    break;
            }
            serverText.setText(msg);
        });

        payOnlineViewModel.getPayOnlineStateEvent().observe(this, event -> {
            switch (event.getState()) {
                case PayOnlineState.SEND:
                    message.setTextColor(getResources().getColor(R.color.pay_idle));
                    message.setText(R.string.pay_wait);
                    String time = payDateFormat.format(new Date());
                    String log = "支付进度: " + time + " - " + event.getMessage();
                    LogUtils.file(TAG, log);

                    if (debugVs != null) return;
                    sendText.setText(log);
                    break;
                case PayOnlineState.SUCCESS:
                    message.setTextColor(getResources().getColor(R.color.pay_success));
                    String successText = event.getMessage();
                    if (TextUtils.isEmpty(successText)) {
                        message.setText(R.string.pay_success);
                        TTSHelper.speak(getString(R.string.pay_success));
                    } else {
                        message.setText(successText);
                        TTSHelper.speak(successText);
                    }
                    qrCodeViewModel.scan();
                    String successTime = payDateFormat.format(new Date());
                    String successLog = "支付成功: " + successTime + " - " + event.getMessage();
                    LogUtils.file(TAG, successLog);

                    if (debugVs != null) return;
                    recText.setText(successLog);
                    break;
                case PayOnlineState.FAILED:
                    message.setTextColor(getResources().getColor(R.color.pay_failed));
                    String failedText = event.getMessage();
                    if (TextUtils.isEmpty(failedText)) {
                        message.setText(R.string.pay_failed);
                        TTSHelper.speak(getString(R.string.pay_failed));
                    } else {
                        message.setText(failedText);
                        TTSHelper.speak(failedText);
                    }
                    qrCodeViewModel.scan();
                    String failedTime = payDateFormat.format(new Date());
                    String failedLog = "支付失败: " + failedTime + " - " + event.getMessage();
                    LogUtils.file(TAG, failedLog);

                    if (debugVs != null) return;
                    recText.setText(failedLog);
                    break;
            }
        });

        payOfflineViewModel.getPayOfflineStateEvent().observe(this, event -> {
            switch (event.getState()) {
                case PayOfflineState.MARK:
                    message.setTextColor(getResources().getColor(R.color.pay_success));
                    String successText = event.getMessage();
                    if (TextUtils.isEmpty(successText)) {
                        message.setText(R.string.pay_success);
                        TTSHelper.speak(getString(R.string.pay_success));
                    } else {
                        message.setText(successText);
                        TTSHelper.speak(successText);
                    }
                    qrCodeViewModel.scan();
                    break;
            }
        });

        qrCodeViewModel.getQRCodeStateEvent().observe(this, event -> {
            switch (event.getState()) {
                case QRCodeState.IDLE:
                    message.setTextColor(getResources().getColor(R.color.pay_idle));
                    message.setText(R.string.pay_idle);
                    break;
                case QRCodeState.SCANNING:
                    message.setTextColor(getResources().getColor(R.color.pay_idle));
                    message.setText(R.string.qrcode_scanning);
                    break;
                case QRCodeState.SUCCESS:
                    TTSHelper.speak("beep");
                    PayData data = event.getData();
                    if (data == null) {
                        LogUtils.w(TAG, "扫码成功,但任务创建失败");
                        return;
                    }
                    if (SCWebSocketClient.getInstance().isOpen()) {
                        payOnlineViewModel.exec(data);
                    } else {
                        payOfflineViewModel.exec(data);
                    }
                    
                    if (debugVs != null) return;
                    String msg = String.format(Locale.getDefault(), getString(R.string.qrcode_text), data.getPayCode());
                    if (data.getTerminalType() != null) {
                        msg += String.format(Locale.getDefault(), getString(R.string.terminal_type_text), data.getTerminalType());
                    }
                    qrCodeText.setText(msg);
                    break;
                case QRCodeState.FAILED:
                    TTSHelper.speak("无效二维码");
                    qrCodeViewModel.scan();
                    break;
                case QRCodeState.ILLEGAL:
                    message.setVisibility(View.GONE);
                    sendText.setTextColor(getResources().getColor(R.color.serial_port_error));
                    sendText.setText(R.string.serial_port_init_failed);
                    String logMessage = String.format(Locale.getDefault(), getString(R.string.log_dir), LogUtils.getConfig().getDir());
                    recText.setTextColor(getResources().getColor(R.color.serial_port_error));
                    recText.setText(logMessage);
                    break;
            }
        });

        commandViewModel.getCommandStateEvent().observe(this, event -> {
            switch (event.getState()) {
                case CommandState.IDLE:
                    settingLayout.animate().setDuration(300).alpha(0f);
                    break;
                case CommandState.WAIT:
                    settingLayout.animate().setDuration(300).alpha(1f);
                    break;
                case CommandState.SUCCESS:
                case CommandState.FAILED:
                    settingText.setText(event.getMessage());
                    settingLayout.animate().setDuration(300).alpha(1f);
                    break;
            }
        });

        // 注意webSocketViewModel的初始化必须在SCWebSocketClient之后
        payOnlineViewModel.initialize();
        payOfflineViewModel.initialize();
        qrCodeViewModel.initialize();
        commandViewModel.initialize();
        handler.post(updateTimeRunnable);

        SCWebSocketClient.getInstance().tryConnect();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        TTSHelper.release();
        SCWebSocketClient.getInstance().close();
        SCTaskExecutor.getInstance().quit();
    }

    private void initViews() {
        debugVs = findViewById(R.id._debug_vs);
        settingLayout = findViewById(R.id._setting);
        logoLayout = findViewById(R.id._logo);
        settingImg = findViewById(R.id._setting_img);
        socketConnectedTime = findViewById(R.id._time);
        settingText = findViewById(R.id._setting_msg);
        message = findViewById(R.id._message);

        settingLayout.setAlpha(0);
        settingImg.post(() -> {
            Animation anim = new RotateAnimation(0f, 360f, 0.5f * settingImg.getWidth(), 0.5f * settingImg.getHeight());
            anim.setDuration(2000);
            anim.setRepeatMode(Animation.RESTART);
            anim.setRepeatCount(-1);
            settingImg.startAnimation(anim);
        });

        if (!BuildConfig.FLAVOR.equals("pro")) {
            inflateDebugLayout();
        }
    }

    private void inflateDebugLayout() {
        if (debugVs == null) return;
        View layout = debugVs.inflate();
        debugLayout = layout.findViewById(R.id._debug);
        snText = layout.findViewById(R.id._sn);
        versionText = layout.findViewById(R.id._version);
        sendText = layout.findViewById(R.id._send_msg);
        recText = layout.findViewById(R.id._recv_msg);
        qrCodeText = layout.findViewById(R.id._qrcode);
        serverText = layout.findViewById(R.id._server);
        ethText = layout.findViewById(R.id._eth);
        wifiText = layout.findViewById(R.id._wifi);
        debugVs = null;

        debugLayout.setVisibility(View.VISIBLE);
        logoLayout.setVisibility(View.GONE);
        debugLayout.setOnClickListener(this);
        logoLayout.setOnClickListener(this);
        refreshPackageInfo();
    }

    private void refreshEthInfo() {
        String ethMac = EthernetHelper.getMacAddress();
        String ethIP = EthernetHelper.getIpString();
        ethText.setText(String.format(Locale.getDefault(), getString(R.string.eth_text), ethMac, ethIP));
    }

    private void refreshWifiInfo() {
        String wifiMac = WifiHelper.getMacAddress();
        String wifiSSID = WifiHelper.getSSID();
        String wifiIP = WifiHelper.getIpString();
        wifiText.setText(String.format(Locale.getDefault(), getString(R.string.wifi_text), wifiMac, wifiSSID, wifiIP));
    }

    private void refreshPackageInfo() {
        String versionName = "";
        String versionCode = "";
        PackageInfo pi;
        try {
            pi = getPackageManager().getPackageInfo(getPackageName(), 0);
            versionName = pi.versionName.toUpperCase();
            versionCode = String.valueOf(pi.versionCode);
        } catch (PackageManager.NameNotFoundException e) {
            LogUtils.w(TAG, "无法获取app信息");
        }
        versionText.setText(String.format(Locale.getDefault(),
                getString(R.string.version_text), versionName, versionCode));
        snText.setText(String.format(Locale.getDefault(), getString(R.string.sn_text), deviceSN));
    }

    private Runnable updateTimeRunnable = new Runnable() {
        @Override
        public void run() {
            socketConnectedTime.setText(socketConnectedTimeDateFormat.format(new Date()));
            handler.postDelayed(this, 1000);
        }
    };

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        LogUtils.i(TAG, "按键KeyCode: " + keyCode);
        switch (keyCode) {
            case KeyEvent.KEYCODE_PAGE_UP:
                audioManager.adjustStreamVolume(
                        AudioManager.STREAM_SYSTEM,
                        AudioManager.ADJUST_RAISE,
                        AudioManager.FLAG_SHOW_UI);
                break;
            case KeyEvent.KEYCODE_PAGE_DOWN:
                audioManager.adjustStreamVolume(
                        AudioManager.STREAM_SYSTEM,
                        AudioManager.ADJUST_LOWER,
                        AudioManager.FLAG_SHOW_UI);
                break;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id._debug:
                debugLayout.setVisibility(View.GONE);
                logoLayout.setVisibility(View.VISIBLE);
                break;
            case R.id._logo:
                debugLayout.setVisibility(View.VISIBLE);
                logoLayout.setVisibility(View.GONE);
                break;
        }
    }
}