<?php

namespace App\Http\Controllers\Api\Customers;

use App\Common\MQ\HttpProducer;
use App\Modules\Enums\ErrorCode;
use App\Exceptions\Api\ApiException;
use App\Modules\Enums\WebsocketType;
use App\Modules\Models\Machine\Machine;
use App\Modules\Models\Production\Production;
use App\Modules\Onenet\Enum\DataStreams;
use App\Modules\Onenet\Facades\Onenet;
use App\Modules\Onenet\Message\MessageKit;
use App\Modules\Onenet\Message\MsgType;
use App\Modules\Onenet\Message\ReceiveMessage\BatteryInfo;
use App\Modules\Onenet\Message\ReceiveMessage\BatteryMessage;
use App\Modules\Onenet\Message\ReceiveMessage\ConnectMessage;
use App\Modules\Onenet\Message\ReceiveMessage\ReturnMessage;
use App\Modules\Onenet\Message\ReceiveMessage\SellStateMessage;
use App\Modules\Onenet\Message\ReceiveMessage\SigQtyMessage;
use App\Modules\Onenet\Message\ReceiveMessage\TakeOutMessage;
use App\Modules\Onenet\Message\ReceiveMessage\VersionMessage;
use App\Modules\Util\PushUtil;
use App\Repositories\Customers\MachineRepository;
use App\Repositories\Customers\PowerRepository;
use App\Repositories\Customers\ProductionRepository;
use App\Repositories\Customers\RentRepository;
use App\Repositories\Customers\WeChatRepository;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Log;
 

 
/**
 * SEND/RECEIVE onenet commands
 * Class OnenetController
 * @package App\Http\Controllers\Api
 */
class OnenetController extends Controller
{
    /**
     * @var RentRepository
     */
    protected $rent;

    protected $power;

    protected $machine;
    protected $weChat;
    protected $production;


    /**
     * RentController constructor.
     * @param RentRepository $rent
     */
    public function __construct(ProductionRepository $production, WeChatRepository $weChat, RentRepository $rent, PowerRepository $power, MachineRepository $machine)
    {
        $this->rent = $rent;
        $this->power = $power;
        $this->machine = $machine;
        $this->weChat = $weChat;
        $this->production = $production;

    }

    /**
     * @param $input
     * @return string
     */
    public function handleOnenetVerification($input)
    {
        $message_valid = Onenet::checkSignature($input['msg'], $input['nonce'], $input['signature']);

        if ($message_valid) {
            return $input['msg'];
        }

        return '';
    }

    /**
     * @param Request $request
     * @return string
     */
    public function messageHandler(Request $request)
    {
        $input = $request->all();
        Log::info("[MessageHandle] params" . json_encode($input));

        if ($request->isMethod('get')) {
            return $this->handleOnenetVerification($input);
        }

        try {
            $message_valid = Onenet::checkSignature($input['enc_msg'], $input['nonce'], $input['msg_signature']);

            if (!$message_valid) {
                Log::info("message is invalid, return");
                throw new ApiException(ErrorCode::MESSAGE_INVALID, trans('api.error.message_invalid'));
            }
            $msg = Onenet::decryptMessage($input['enc_msg']);

            if ($msg == '') {
                Log::info("message decrypt failed, return");
                throw new ApiException(ErrorCode::MESSAGE_INVALID, trans('api.error.message_invalid'));
            }
            Log::info("[MessageHandle] decrypted message is" . $msg);


            $messageKit = new MessageKit();
            $msg = $messageKit->unpack(json_decode($msg, true));

            //for test only
//            $messageKit = new MessageKit();
//            $msg = $messageKit->unpack($input['msg']);
            if ($msg != null) {
                $this->processMessage($msg);
            }

        } catch (\Exception $e) {
            if ($e instanceof ApiException) {
                Log::info("Api Exception occured:" . $e->getMessage());
            }

            Log::info("Exception occured:" . $e->getMessage());
        } catch (\Throwable $t) {
            Log::info("Exception occured:" . $t->getMessage());
        }

        return isset($input['enc_msg']) ? $input['enc_msg'] : '';
    }

    /**
     * @param $msg
     */
    private function processMessage($msg)
    {
        switch ($msg->getMsgType()) {
            case MsgType::CONNECT_TYPE:
                $this->connectMessageHandler($msg);
                break;
            case MsgType::DATASTREAM_TYPE:


                switch ($msg->getDataStream()) {
                    case DataStreams::BATTERY:
                        $this->batteryMessageHandler($msg);
                        break;
                    case DataStreams::TAKEOUT:
                        $this->takeOutMessageHandler($msg);
                        break;
                    case DataStreams::BATTERY_RETURN:
                        $this->returnMessageHandler($msg);
                        break;
                    case DataStreams::SELL_STATE:
                        $this->sellStateMessageHandler($msg);
                        break;
                    case DataStreams::VERSION:
                        $this->versionMessageHandler($msg);
                        break;
                    case DataStreams::SIGQTY:
                        $this->SigQtyMessageHandler($msg);
                        break;
                }


                break;
        }
    }

    /**
     * @param ConnectMessage $msg
     */
    private function connectMessageHandler(ConnectMessage $msg)
    {
        //update machine network status
        Log::info('[connectMessageHandler] msg:' . $msg->getDeviceId() . '_' . $msg->getStatus());
        $this->machine->setLifeByMacide($msg->getDeviceId(), $msg->getStatus());
    }

    /**
     * @param VersionMessage $msg
     */
    private function versionMessageHandler(VersionMessage $msg)
    {
        //update machine version
        //version
        Log::info('[versionMessageHandler] msg:' . $msg->getDeviceId() . '_' . $msg->getVersion());
        $this->machine->setVersionByMacide($msg->getDeviceId(), $msg->getVersion());

    }

    /**
     * @param SigQtyMessage $msg
     */
    private function SigQtyMessageHandler(SigQtyMessage $msg)
    {
        //update machine status
        //iccid
        Log::info('[SigQtyMessageHandler] msg:' . $msg->getDeviceId() . '_' . $msg->getIccid());

        $this->machine->setIccidByMacide($msg->getDeviceId(), $msg->getIccid());
    }

    /**
     * 处理电池信息
     * @param BatteryMessage $msg
     */
    private function batteryMessageHandler(BatteryMessage $msg)
    {
        //
        Log::info('[batteryMessageHandler] msg:begin');
        $batteries = $msg->getBatteryInfo();
        if (count($batteries)) {
            $machine = $this->machine->getByMacide($msg->getDeviceId());
            foreach ($batteries as $battery) {
                $initial_sn = $battery->getBatterySn();
                $pType = substr($initial_sn, 2, 2);//讲解器类型
                if($pType != 'E2'){
                    $pType = hex2bin($pType);    
                }
                
                $sn = substr($initial_sn, 4);//新的sn
                $season = $this->power->getSeason($pType);
                Log::info('[setBatteryStatus] msg:' . $machine->id . '_' . $battery->getPosition() . '_' . $battery->getBatterySn() . '_' . $battery->getBatteryLife() . '_' . $pType);
                $this->power->setPowerStatus($machine->id, $battery->getPosition(), $sn, $battery->getBatteryLife(), $pType, $season);
            }
        }
    }

    /**
     * 处理拿出充电宝命令
     * @param TakeOutMessage $msg
     */
    private function takeOutMessageHandler(TakeOutMessage $msg)
    {
        Log::info("[takeOutMessageHandler] msg");
        $initial_sn = $msg->getBatterySn();
        $sn = substr($initial_sn, 4);//新的sn
        $this->dotakeOut($sn);

        //send take out
    }

    private function sellStateMessageHandler(SellStateMessage $msg)
    {
        $storage1 = $msg->getFirstStorageStatus();
        $storage2 = $msg->getSecondStorageStatus();

        if ($storage1 == 0 && $storage2 <= 1) {
            //send lack of headset
        }
    }

    private function dotakeOut($power_no)
    {
        $power_obj = $this->power->takeOutSuccess($power_no);
        if($power_obj->status == 1){
            Log::info("[doTakeout] status=>1:" . json_encode($power_obj));
            exit();
        }
        $product_obj = $this->rent->takeOutSuccess($power_obj);
        $rent_obj = $product_obj->rent()->first();
        $out = $this->rent->isLastOut($rent_obj, $product_obj);
        if ($out['status']) {
            //通知websocket
            $data = [
                'type' => WebsocketType::POWER_TAKE_OUT_SUCCESS,
                'success_hatch_nos' => $out['hatch_nos'],
                'fail_hatch_nos' => [],
            ];
            Log::info("[doTakeout] webscoket:" . json_encode($data));
            PushUtil::pushWebsocketMessage($rent_obj->customer_id, $data);
        }
    }

    /**
     * 处理归还充电宝命令
     * $mac_ide机柜onenet编号,
     * $hatch_no仓口号,
     * $power_no充电宝号,
     * $has_power电量
     * @param ReturnMessage $msg
     * @return mixed
     */
    private function returnMessageHandler(ReturnMessage $msg)
    {
        Log::info("[returnMessageHandler] bengin:");

        $initial_sn = $msg->getBatterySn();
        // $sn = substr($initial_sn, 4);//新的sn
        $this->doReturn($msg->getDeviceId(), $msg->getBatteryPosition(), $initial_sn, $msg->getBatteryLife(), '');
    }

    /**
     * @param $mac_ide
     * @param $hatch_no
     * @param $power_no
     * @param $has_power
     */
    private function doReturn($mac_ide, $hatch_no, $power_no, $has_power, $backTime = '', $isT = 1)
    {

        $machine_obj = $this->machine->getByMacide($mac_ide);

        $power_obj = $this->power->doReturn($machine_obj, $power_no, $hatch_no, $has_power, $isT);
        if($power_obj == null){
            exit();
        }
        $res = $this->rent->doReturn($power_obj, $machine_obj, $hatch_no, $backTime);
        $rent_obj = $this->rent->getRent($res['out_trade_no']);
        $this->weChat->returnAllMes($rent_obj);
        switch ($res['status']) {
            case 1:
                $this->weChat->doRefund(array_except($res, ['status']));
                break;
            default:
                break;
        }
    }


    public function orderExpire(Request $request)
    {
        Log::info("[orderExpire] params:" . json_encode($request->all()));

        $rent_obj = $this->rent->getRentDetail($request->get('order_id'));


        $success_hatchs = [];
        $fail_hatchs = [];
        foreach ($rent_obj->production as $production) {
            if ($production->is_out) {
                //发货成功
                $success_hatchs[] = $production->rent_hatch_no;
            } else {
                //发货失败
                $fail_hatchs[] = $production->rent_hatch_no;
                $refound_no = $this->production->makeProductionRefund($production);
                $this->weChat->productOutFailRefund($rent_obj, $refound_no);
                $rent_obj->back_money += $rent_obj->deposit;
            }

        }

        if ($rent_obj->back_money) {
            $rent_obj->save();
        }
        //处理账户金额变动
        $this->rent->changeCustomerDeposit(2, $rent_obj->customer_id, $rent_obj->deposit * count($fail_hatchs));
        //通知workman

        if ($fail_hatchs) {
            $data = [
                'type' => WebsocketType::POWER_TAKE_OUT_FAIL,
                'success_hatch_nos' => $success_hatchs,
                'fail_hatch_nos' => $fail_hatchs,
            ];
            PushUtil::pushWebsocketMessage($rent_obj->customer_id, $data);
        }
//        else {
//            $data = [
//                'type' => WebsocketType::POWER_TAKE_OUT_SUCCESS,
//                'hatch_nos' => $success_hatchs,
//            ];
//        }

        //取货结果通知
        $this->weChat->shipmentMes($rent_obj,$success_hatchs);

        //标记订单已处理
        $this->rent->expireHandleStatusChange($rent_obj,$fail_hatchs);

        return $this->responseSuccess();



        //check order status
    }

    public function manualReturn(Request $request)
    {
        $machine = $this->machine->get($request->input('machine'), true);
//        return json_encode(['id'=>$machine->he_cloud_device_id, 'hatch_no'=>$request->input('hatch_no'), 'power_no'=>$request->input('power_no'), 'has_power'=>$request->input('has_power')]);

        return $this->doReturn($machine->he_cloud_device_id, $request->input('hatch_no'), $request->input('power_no'), $request->input('has_power'), $request->input('backTime'), $request->input('isTrue'));
    }

    public function testOpenMultiple()
    {
        $machine = Machine::first();

//        Onenet::openMultiple($machine, '11111', [4,5]);

        Onenet::produceMessage(1, config('constants.mq.open_topic'));
    }
}