import './Shower.scss';
import Taro, { Component, Config } from '@tarojs/taro';
import { View, Image, Text } from '@tarojs/components';
import ShowerStart from '../../images/shower/ic_sel@2x.png';
import ShowerCantStart from '../../images/shower/ic_nor@2x.png';
import LineImg from '../../images/shower/img_nor@2x.png';
import ScanIcon from '../../images/shower/ic_saoma@2x.png';

import { shareHandle } from '@/common/shareMethod';
import {
  fetchShowerInfo,
  DeviceInfoResponse,
  startShowerEquipment,
  fetchUsingShowerInfo,
  getShowerController,
  OperationMode,
  ControllerResponse,
  ShowerUseType,
} from '@/api/shower';
import { connect, useSelector } from '@tarojs/redux';
import { Customer } from '@/types/Customer/Customer';
import { ComponentClass } from 'react';
import { connectSocket } from '@/api/socket';
import { SOCKET_URL } from '@/constants';
import { BluetoothDevice } from '@/types/Shower/Shower';
import { updateBluetoothDevice } from './actions';
import { ab2str, str2ab } from '@/utils/arrayBuffer';
import WaitPayOrderComponent from '@/components/WaitPayOrder/WaitPayOrder';
import { PayOrderState } from '@/store/rootReducers/orderState';

enum BlueToothError {
  BlueToothNotOpen = 'BlueToothNotOpen',
  DeviceNotFound = 'DeviceNotFound',
}

type DeviceInfo = {
  deviceId: string;
  uuid: string;
  isPrimary: boolean;
  serviceId: string;
  notifyId: string;
  writeId: string;
  readId: string;
};
type PageStateProps = {
  userinfo: Customer;
  bluetoothDevice: BluetoothDevice;
  showerControlConfig: ControllerResponse;
};
type PageDispatchProps = {
  updateBluetoothDevice: (entity: BluetoothDevice) => void;
};

type IProps = PageStateProps & PageDispatchProps;

type PageState = {
  bluetoothState: boolean;
  deviceInfo: DeviceInfo;
  sockedDone: boolean;
  deviceDone: boolean;
  showerState: boolean;
  showAppoint: boolean;
};
const StopCode = 1000;

interface Shower {
  props: IProps;
  state: PageState;
}

let timer: NodeJS.Timeout | null = null;
let reConnectting: boolean = false;
let socketTask: Taro.SocketTask | null = null;
@connect(
  ({ userinfo, Shower, showerState }) => ({
    userinfo,
    bluetoothDevice: Shower,
    showerControlConfig: showerState.controllerConfigs,
  }),
  dispatch => ({
    updateBluetoothDevice(data: BluetoothDevice) {
      dispatch(updateBluetoothDevice(data));
    },
  }),
)
class Shower extends Component {
  config: Config = {
    navigationBarTitleText: '洗 浴',
  };

  constructor(props) {
    super(props);
    this.state = {
      bluetoothState: false,
      sockedDone: false,
      deviceDone: false,
      showerState: false,
      deviceInfo: {
        deviceId: '',
        uuid: '',
        isPrimary: false,
        serviceId: '',
        notifyId: '',
        writeId: '',
        readId: '',
      },
      showAppoint: false,
    };
  }

  onShareAppMessage = shareHandle;

  componentWillMount() {
    // useType;
    this.openBluetooth();
    this.connectDeviceSocket();
    this.checkUsingDevice();
    const { showerControlConfig } = this.props;
    if (showerControlConfig.useType === ShowerUseType.mix) {
      this.setState({
        showAppoint: true,
      });
    }
  }

  componentWillUnmount() {
    this.closeBluetooth();
    Taro.closeSocket();
    timer && clearTimeout(timer);
    timer = null;
    reConnectting = false;
  }

  connectDeviceSocket() {
    connectSocket(SOCKET_URL).then(task => {
      socketTask = task;
      task.onOpen(() => {
        console.log('onOpen');
        if (reConnectting) {
          this.reConnectDeviceSocket(true);
        }
        this.setState({
          sockedDone: true,
        });
        task.send({ data: str2ab('{}') });
        this.sendDeviceCode();
      });
      task.onMessage(res => {
        const msg: string = ab2str(res.data);
        console.log('socket onMessage: ', msg);
        if (msg === '[0]') {
          console.log('结束蓝牙以及socket: ', msg);
          this.closeDeviceSocket();
          this.closeBluetoothConnection();
        } else if (msg === '[]') {
        } else {
          if (msg.length > 100) {
            this.sendMessageToDevice('[]');
          } else if (msg.length > 20) {
            for (let index = 0; index <= Math.floor(msg.length / 20); index++) {
              let str = msg.substring(index * 20, (index + 1) * 20);
              this.sendMessageToDevice(str);
            }
          } else {
            if (msg) {
              this.sendMessageToDevice(msg);
            }
          }
        }
      });
      task.onClose(e => {
        console.log('socked关闭', e, reConnectting, timer);
        this.setState({
          sockedDone: false,
        });
        socketTask = null;
        if (e.code === StopCode) {
          console.log('正确结束socket连接');
        } else {
          console.log('开始重连socket');
          this.reConnectDeviceSocket(false);
        }
      });
    });
  }

  reConnectDeviceSocket(cancel: boolean) {
    if (cancel) {
      timer && clearTimeout(timer);
      timer = null;
      reConnectting = false;
      return;
    }
    console.log(reConnectting, timer);

    if (reConnectting) {
      this.connectDeviceSocket();
    } else if (timer) {
      clearTimeout(timer);
      reConnectting = false;
      timer = null;
      console.log('请保证网络正常');
      Taro.showModal({
        title: '警告',
        content: '请保持网络畅通正常',
      });
    } else {
      timer = setTimeout(() => {
        reConnectting = false;
      }, 10000);
      reConnectting = true;
      console.log(reConnectting, timer);
      this.connectDeviceSocket();
    }
  }

  sendDeviceCode() {
    const { sockedDone, deviceDone } = this.state;
    const { bluetoothDevice } = this.props;
    console.log('socket状态：', sockedDone, '蓝牙状态：', deviceDone);

    if (sockedDone && socketTask && deviceDone) {
      let deviceData = '{<' + bluetoothDevice.code + '>}';
      console.log('<---发送设备编号:', deviceData);
      socketTask.send({
        data: str2ab(deviceData),
        success: msg => {
          console.log('发送设备编号:', msg, '--->');
        },
        fail: err => {
          console.log('发送设备编号:', err, '--->');
        },
      });
    }
  }

  closeDeviceSocket() {
    console.log('in close', socketTask, timer);
    if (socketTask) {
      socketTask.close({
        code: StopCode,
        complete: () => {
          socketTask = null;
        },
      });
    }
    if (timer) {
      clearTimeout(timer);
    }
  }

  checkUsingDevice() {
    const { userinfo, updateBluetoothDevice } = this.props;
    fetchUsingShowerInfo({
      customerId: userinfo.customerId,
    })
      .then(res => {
        const data: DeviceInfoResponse | null = res.data;
        if (data) {
          updateBluetoothDevice(data);
          this.setState({
            showerState: true,
          });
          if (this.checkBluetoothState()) {
            this.startUseShower(true);
          } else if (data.isOnlyBluetooth) {
            this.warnBluetoothOpen();
          } else {
            this.startUseShower(true);
          }
        }
      })
      .catch(err => {
        console.error(err);
      });
  }

  openBluetooth() {
    Taro.openBluetoothAdapter()
      .then(res => {
        console.log(res);
        this.setState({
          bluetoothState: true,
        });
      })
      .catch(err => {
        console.error(err);
        Taro.onBluetoothAdapterStateChange(res => {
          console.log('adapterState changed, now is', res);
          if (res.available) {
            this.setState({
              bluetoothState: true,
            });
          }
        });
      });
  }

  checkBluetoothState() {
    const { bluetoothState } = this.state;
    return bluetoothState;
  }

  closeBluetoothConnection() {
    const { deviceInfo } = this.state;
    this.setState({
      showerState: false,
    });
    Taro.closeBLEConnection({
      deviceId: deviceInfo.deviceId,
    })
      .then(() => {
        this.setState({
          deviceDone: false,
        });
      })
      .catch(err => {
        console.log('断开蓝牙连接失败！');
        console.error(err);
      });
  }

  closeBluetooth() {
    Taro.closeBluetoothAdapter();
  }

  warnBluetoothOpen() {
    return Taro.showModal({
      title: '警告',
      content: '为保障正常使用，请打开蓝牙功能',
    });
  }

  scanEquipment() {
    return Taro.scanCode({
      onlyFromCamera: true,
      scanType: ['qrCode'],
    })
      .then(res => {
        console.log(res);
        const { result } = res;
        const { userinfo, updateBluetoothDevice } = this.props;

        return fetchShowerInfo({
          deviceCode: result,
          customerId: userinfo.customerId.toString(),
          campusId: userinfo.areaId,
        })
          .then(res => {
            const data = res.data;
            updateBluetoothDevice({
              code: data.code,
              isOnlyBluetooth: data.isOnlyBluetooth,
              position: data.position,
              operationMode: data.operationMode,
            });
          })
          .catch(err => {
            console.error(err);
            Taro.showToast({
              title: err.msg || '请扫描正确的设备码',
              icon: 'none',
            });
          });
      })
      .catch(err => {
        console.error(err);
        // Taro.showToast({
        //   title: err.msg || '呼起扫码失败！',
        //   icon: 'none',
        // });
      });
  }

  startDevicesDiscovery() {
    if (this.checkBluetoothState()) {
      return Taro.startBluetoothDevicesDiscovery()
        .then(() => {
          const { code, isOnlyBluetooth } = this.props.bluetoothDevice;
          let timer: NodeJS.Timeout | null = null;
          return new Promise((resolve, reject) => {
            if (isOnlyBluetooth) {
              timer = setTimeout(() => {
                reject({ msg: BlueToothError.DeviceNotFound });
              }, 10000);
            } else {
              timer = setTimeout(() => {
                reject({ msg: BlueToothError.DeviceNotFound });
              }, 5000);
            }
            Taro.onBluetoothDeviceFound(res => {
              const devices = res.devices;
              if (devices[0].name === code) {
                let deviceId = devices[0].deviceId;
                Taro.stopBluetoothDevicesDiscovery();
                this.setState({
                  deviceId,
                });
                if (timer) {
                  clearInterval(timer);
                }
                resolve(deviceId);
              }
            });
          });
        })
        .catch(err => {
          // Taro.stopBluetoothDevicesDiscovery();
          if (timer) {
            clearInterval(timer);
          }
          Taro.hideLoading();
          console.error('startBluetoothDevicesDiscovery: ', err);
          // if (err.msg === BlueToothError.DeviceNotFound) {
          Taro.stopBluetoothDevicesDiscovery();
          this.sendStartShower();
          throw err;
          // } else {
          //   return this.warnBluetoothOpen();
          // }
        });
    } else {
      Taro.hideLoading();
      return this.warnBluetoothOpen().then(res => {
        if (res.confirm) {
          const { isOnlyBluetooth } = this.props.bluetoothDevice;
          if (!isOnlyBluetooth) {
            Taro.showLoading({
              title: '开启中',
              mask: true,
            });
            return new Promise((resolve, reject) => {
              setTimeout(() => {
                this.sendStartShower();
                reject();
              }, 5000);
            });
          }
        }
        throw BlueToothError.BlueToothNotOpen;
      });
    }
  }

  createConnection(deviceId: string) {
    console.log(deviceId);
    return new Promise((resolve, reject) => {
      Taro.createBLEConnection({
        deviceId,
      });

      let abStr = '';
      Taro.onBLECharacteristicValueChange(res => {
        console.log(
          `characteristic ${res.characteristicId} has changed, now is ${
            res.value
          }`,
        );
        console.log(res.value);
        let datastr = ab2str(res.value);
        console.log('获取socket消息： ', datastr);
        if (datastr === '<>') {
          console.log('结束蓝牙以及socket: ', datastr);
          this.closeDeviceSocket();
          this.closeBluetoothConnection();
        } else {
          if (
            datastr.substring(0, 1) === '{' &&
            datastr.substring(datastr.length - 1, datastr.length) === '}'
          ) {
            console.log('<---发送完整数据： ', datastr);
            if (socketTask) {
              socketTask.send({
                data: str2ab(datastr),
                success: msg => {
                  console.log('发送完整数据：', msg, '--->');
                },
                fail: err => {
                  console.log('发送完整数据：', err, '--->');
                },
              });
            }
          } else if (datastr.substring(0, 1) === '{') {
            console.log('接受头部数据： ', datastr);
            abStr = datastr;
          } else if (
            abStr.length &&
            datastr.substring(datastr.length - 1, datastr.length) === '}'
          ) {
            abStr += datastr;
            console.log('接受尾部数据后： ', abStr);
            console.log('<---发送完整数据： ', abStr);
            if (socketTask) {
              socketTask.send({
                data: str2ab(abStr),
                success: msg => {
                  console.log('发送完整数据：', msg, '--->');
                },
                fail: err => {
                  console.log('发送完整数据：', err, '--->');
                },
              });
            }
            abStr = '';
          } else {
            console.log('弃掉数据：', datastr);
          }
        }
      });

      Taro.onBLEConnectionStateChange(res => {
        const { showerState } = this.state;
        console.log(
          `device ${res.deviceId} state has changed, connected: ${
            res.connected
          }`,
        );
        if (res.connected) {
          this.setState({
            deviceDone: true,
            showerState: true,
          });
          resolve(res.deviceId);
        } else {
          console.log(res);
          this.setState({
            deviceDone: false,
          });
          if (showerState) {
            this.reConnectDeviceBluetooth();
          }
          reject('蓝牙未成功连接');
        }
      });
    });
  }

  sendMessageToDevice(msg: string) {
    const { deviceInfo } = this.state;
    console.log(msg, str2ab(msg).byteLength);
    if (deviceInfo.writeId) {
      Taro.writeBLECharacteristicValue({
        deviceId: deviceInfo.deviceId,
        serviceId: deviceInfo.serviceId,
        characteristicId: deviceInfo.writeId,
        value: str2ab(msg),
      });
    }
  }

  reConnectDeviceBluetooth() {
    if (this.checkBluetoothState()) {
      const { deviceInfo } = this.state;
      this.createConnection(deviceInfo.deviceId);
    }
  }

  getDeviceServices(deviceId: string) {
    return Taro.getBLEDeviceServices({
      deviceId: deviceId,
    }).then(services => {
      return { deviceId: deviceId, services: services.services };
    });
  }

  getDeviceCharacter({
    deviceId,
    serviceId,
  }: {
    deviceId: string;
    serviceId: string;
  }) {
    return Taro.getBLEDeviceCharacteristics({
      deviceId,
      serviceId,
    }).then(res => {
      let notifyId = '',
        writeId = '',
        readId = '';
      for (let i = 0; i < res.characteristics.length; i++) {
        let charc = res.characteristics[i];
        if (charc.properties.notify) {
          notifyId = charc.uuid;
        }
        if (charc.properties.write) {
          writeId = charc.uuid;
        }
        if (charc.properties.write) {
          readId = charc.uuid;
        }
      }
      return {
        notifyId,
        writeId,
        readId,
      };
    });
  }

  openNotify({ notifyId, writeId, readId }) {
    this.setState(({ deviceInfo }: PageState) => ({
      deviceInfo: {
        ...deviceInfo,
        notifyId,
        writeId,
        readId,
      },
    }));
    const { deviceInfo } = this.state;
    return Taro.notifyBLECharacteristicValueChange({
      deviceId: deviceInfo.deviceId,
      serviceId: deviceInfo.serviceId,
      characteristicId: notifyId,
      state: true,
    }).then(res => {
      console.log(res);

      let abStr = '';
      Taro.onBLECharacteristicValueChange(res => {
        let msg = ab2str(res.value);
        console.log('获取蓝牙设备消息： ', msg);
        if (msg === '<>') {
          this.closeBluetoothConnection();
          this.closeDeviceSocket();
        } else {
          if (
            msg.substring(0, 1) === '{' &&
            msg.substring(msg.length - 1, msg.length) === '}'
          ) {
            console.log('<---发送给socket完整数据： ', msg);
            socketTask &&
              socketTask.send({
                data: str2ab(msg),
                success: msg => {
                  console.log('发送给socket完整数据：', msg, '--->');
                },
                fail: err => {
                  console.log('发送给socket完整数据：', err, '--->');
                },
              });
          } else if (msg.substring(0, 1) === '{') {
            console.log('接受头部数据： ', msg);
            abStr = msg;
          } else if (
            abStr.length &&
            msg.substring(msg.length - 1, msg.length) === '}'
          ) {
            abStr += msg;
            console.log('接受尾部数据后： ', abStr);
            console.log('<---发送给socket完整数据： ', abStr);

            socketTask &&
              socketTask.send({
                data: str2ab(abStr),
                success: msg => {
                  console.log('发送给socket完整数据：', msg, '--->');
                },
                fail: err => {
                  console.log('发送给socket完整数据：', err, '--->');
                },
              });
            abStr = '';
          } else {
            if (abStr.length) {
              abStr += msg;
            } else {
              console.log('弃掉数据：', msg);
            }
          }
        }
      });
    });
  }

  checkUserMoney() {
    const { userinfo } = this.props;
    return getShowerController({
      customerId: userinfo.customerId,
      campusId: userinfo.areaId,
    })
      .then(res => {
        console.log(res);
        const {
          bluetoothDevice: { operationMode },
        } = this.props;
        const { thresholdValue, thresholdPrompt, money, beanAmount } = res.data;
        if (thresholdValue) {
          if (
            operationMode === OperationMode.onlyAimi &&
            money < thresholdValue
          ) {
            return this.userWarnningHandle(thresholdPrompt, money, beanAmount);
          }
          if (
            (operationMode === OperationMode.aimiFirst ||
              operationMode === OperationMode.beanFirst) &&
            money + beanAmount < thresholdValue
          ) {
            return this.userWarnningHandle(thresholdPrompt, money, beanAmount);
          }
          return Promise.resolve();
        }
        return Promise.resolve();
      })
      .catch(console.error);
  }

  userWarnningHandle(thresholdPrompt, money, beanAmount) {
    Taro.hideLoading();
    console.log('in userWarnningHandle');
    let arr = [money, beanAmount];
    let i = -1;
    return Taro.showModal({
      title: '提示',
      content: thresholdPrompt.replace(/(\{.*?\})/g, () => {
        i++;
        return arr[i];
      }),
    }).then(res => {
      if (res.confirm) {
        Taro.showLoading({
          title: '开启中',
          mask: true,
        });
        return Promise.resolve();
      } else {
        return Promise.reject();
      }
    });
  }

  startUseShower(reConnect: boolean) {
    if (!reConnect) {
      Taro.showLoading({
        title: '开启中',
        mask: true,
      });
    }
    const { sockedDone } = this.state;
    if (!sockedDone) {
      this.connectDeviceSocket();
    }
    this.checkUserMoney()
      .then(() => this.startDevicesDiscovery())
      .then((deviceId: string) => this.createConnection(deviceId))
      .then(this.getDeviceServices)
      .then(res => {
        const deviceId = res.deviceId;
        const services = res.services;
        let service = services.find(
          item =>
            item.uuid.toLocaleLowerCase() ===
            '6e401103-b5a3-f393-e0a9-e50e24dcca9e',
        );
        if (!service) {
          service = services[0];
        }
        console.log('获取全部services: ', services, '. serviceId: ', service);
        const serviceId = service.uuid;
        this.setState(({ deviceInfo }: PageState) => ({
          deviceInfo: {
            ...deviceInfo,
            deviceId,
            serviceId,
          },
        }));
        return this.getDeviceCharacter({
          deviceId,
          serviceId,
        });
      })
      .then(res => this.openNotify(res))
      .then(() => {
        this.sendDeviceCode();
        if (!reConnect) {
          setTimeout(() => {
            this.sendStartShower();
          }, 2000);
        }
      })
      .catch(err => {
        console.error(err);
        Taro.hideLoading();
      });
  }

  sendStartShower() {
    const { code } = this.props.bluetoothDevice;
    const { userinfo } = this.props;
    startShowerEquipment({
      deviceCode: code,
      customerId: userinfo.customerId.toString(),
      customerName: userinfo.customerName,
      customerPhone: userinfo.customerPhone,
    })
      .then(res => {
        console.log(res);
        Taro.showToast({
          title: '开启成功！',
        });
        this.setState({
          showerState: true,
        });
      })
      .catch(err => {
        console.error(err);
        Taro.showToast({
          title: err.msg || '开启失败！',
          icon: 'none',
        });
        this.closeBluetoothConnection();
      });
  }
  goAppointShower() {
    Taro.redirectTo({
      url: '/pages/Shower/ShowerAppointment',
    });
  }

  render() {
    const {
      bluetoothDevice: { code, position },
    } = this.props;
    const { showerState, showAppoint } = this.state;
    const payOrderState = useSelector(
      (state: { orderState: PayOrderState }) =>
        state.orderState.waitPayOrderState,
    );

    return (
      <View className='Shower'>
        <WaitPayOrderComponent />
        {showAppoint && (
          <View className='showAppoint' onClick={this.goAppointShower}>
            预约洗浴
          </View>
        )}
        <View className={payOrderState ? 'blur' : ''}>
          <View className='equipment-info-box'>
            <View className='equipment-info'>
              {code ? (
                <View className='equipment-title matched'>已匹配设备</View>
              ) : (
                <View className='equipment-title'>未匹配设备</View>
              )}

              <Image className='equipment-line' src={LineImg} />
              {code ? (
                <View className='equipment-list'>
                  <View className='equipment-item'>
                    <Text className='equipment-label'>设备编号：</Text>
                    <Text className='equipment-value'>{code}</Text>
                  </View>
                  <View className='equipment-item'>
                    <Text className='equipment-label'>设备位置：</Text>
                    <Text className='equipment-value'>{position}</Text>
                  </View>
                </View>
              ) : (
                <View v-loading='loading' className='equipment-noCode'>
                  点击左下角扫一扫完成匹配
                </View>
              )}
            </View>
          </View>
          <View className='btn-box'>
            <Image
              className='shower-scan-btn'
              src={ScanIcon}
              onClick={this.scanEquipment}
            />
            {code && !showerState ? (
              <Image
                className='shower-start-btn'
                src={ShowerStart}
                onClick={() => this.startUseShower(false)}
              />
            ) : (
              <Image className='shower-start-btn' src={ShowerCantStart} />
            )}
          </View>
        </View>
      </View>
    );
  }
}

export default Shower as ComponentClass<PageStateProps, PageState>;
