<?php

namespace app\controller\Bus;

use app\controller\Aes;
use app\controller\HardMessage;
use app\controller\HMACMD5;
use app\model\ChargeOrder as ChargeOrderModel;
use app\model\Vinlicense;
use app\Request;
use DateInterval;
use DateTime;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Overtrue\Pinyin\Pinyin;
use think\facade\Db;
use think\facade\Log;


class PlugCharge
{
    public function insert_start_charge(Request $request)
    {
        $params = file_get_contents('php://input');
        $params = json_decode($params, true);
        $OperatorID = empty($params['OperatorID']) ? '' : $params['OperatorID'];
        $authorization = $request->header()['authorization'];//传递过来的token
        $auths = explode(' ', trim($authorization));
        $auth = $auths[count($auths) - 1];
        $checkParams = $this->checkParams($params);
        if ($checkParams != 2)
            return json(['Msg' => $checkParams . '为空', 'Ret' => 4003, 'Data' => ['Status' => 1]]);
        if ($auth) {
            if ($this->checkToken_assess($auth) == 1) {
                $data = json_encode(['Status' => 1]);
                $data = $this->Encrypt($data);
                $seq = empty($Seq) ? '0001' : $params['Seq'];
                $datetime = empty($TimeStamp) ? date('YmdHis') : $params['TimeStamp'];
                $sig = $OperatorID . $data . $datetime . $seq;
                $s = $this->HmacMD5($sig);
                return json(['Msg' => 'Token过期或者无效', 'Ret' => 4002, 'Data' => $data, 'Sig' => $s]);
            }
            $checkSig = $this->checkSig($params['OperatorID'], $params);
            if ($checkSig == 1) {
                $data = json_encode(['Status' => 1]);
                $data = $this->Encrypt($data);
                $seq = empty($Seq) ? '0001' : $params['Seq'];
                $datetime = empty($TimeStamp) ? date('YmdHis') : $params['TimeStamp'];
                $sig = $OperatorID . $data . $datetime . $seq;
                $s = $this->HmacMD5($sig);
                return json(['Msg' => '签名错误', 'Ret' => 4001, 'Data' => $data, 'Sig' => $s]);
            }
            $data = $this->Decrypt($params['Data']);
            $data = json_decode($data, true);
            if (isset($data['vin']) && isset($data['connectorID'])) {
                Log::write("The Plug Message come on :" . $data['vin'] . $data['connectorID']);
                $CheckVinIsExist = Db::table('vinlicense')->where('EvcInfoNo', $data['vin'])->find();
                if (!empty($CheckVinIsExist)) {
                    $AppointmentTime = Db::table('appointment_time')->where('id', 1)->value('AppointmentTime');
                    $currentDate = new DateTime();
                    $combinedDateTime = new DateTime($currentDate->format('Y-m-d') . ' ' . $AppointmentTime);
                    $AppointmentTime = $combinedDateTime->format('Y-m-d H:i:s');
                    $nowTime = $currentDate->format('Y-m-d H:i:s');
                    $nowDay = $currentDate->format('Y-m-d');
                    $check = Db::table('appointment')->whereDay('action_time', $nowDay)->where('Vin', $data['vin'])->where('ConnectorID', $data['connectorID'])->find();
                    if (empty($check)) {
                        Db::table('appointment')->save(['action_time' => $nowTime, 'ConnectorID' => $data['connectorID'], 'Vin' => $data['vin'], 'plug_time' => $AppointmentTime]);
                    }
//                    $check = Db::table('appointment')->whereDay('action_time', $nowDay)->where('Vin', $data['vin'])->find();
//                    if (empty($check)) {
//                        Db::table('appointment')->save(['action_time' => $nowTime, 'ConnectorID' => $data['connectorID'], 'Vin' => $data['vin'], 'plug_time' => $AppointmentTime]);
//                    } else {
//                        $checkVin = Db::table('appointment')->whereDay('action_time', $nowDay)->where('Vin', $data['vin'])->value('ConnectorID');
//                        if ($checkVin != $data['connectorID']) {
//                            Db::table('appointment')->whereDay('action_time', $nowDay)->where('Vin', $data['vin'])->save(['action_time' => $nowTime, 'ConnectorID' => $data['connectorID']]);
//                        }
//                    }
                }
                $kk['SuccStat'] = 0;
                $kk['FailReason'] = 0;
                return $this->encodeData($kk, $params, 0);
            } else {
                $kk['SuccStat'] = 1;
                $kk['FailReason'] = 1;
                return $this->encodeData($kk, $params, 0);
            }
        }
        return $this->encodeData(['Status' => 1], $params, 4002, 'Bearer 未添加');
//        $OperatorID = Config::get('hard.OperatorID');
//        $OperatorSecret = Config::get('hard.OperatorSecret');
    }

    public function PlugGennerateOrder($ConnectorID, $vin): array
    {
        $station_id = Db::table('charge_pile')->where('ConnectorID', $ConnectorID)->value('charge_station_id');
        $EquipAuthSeq = Db::table('charge_pile')->where('ConnectorID', $ConnectorID)->value('EquipAuthSeq');
        $no = Db::table('charge_pile')->where('ConnectorID', $ConnectorID)->value('no');
        if (empty($EquipAuthSeq)) {
            $EquipAuthSeq = $this->generate_EquipmentAuth(7);
            Db::table('charge_pile')->where('ConnectorID', $ConnectorID)->update(['EquipAuthSeq' => $EquipAuthSeq]);
        }
        $Hard = new HardMessage();
        $openid = 'o-wLg5DGWU_rYwfmp1-1Wck1lKGw';
        $area = Db::table('user')->where('openid', $openid)->value('area');
        $pinyin = new Pinyin();
        $s = $pinyin->sentence($area);
        $s = strtr($s, array(' ' => ''));
        $table = 'charge_order_' . $s;
        $table_user = 'user_' . $s;
        $AuthMessage = $Hard->Get_query_equip_auth($EquipAuthSeq, $ConnectorID);
        if (isset($AuthMessage['FailReason']) && isset($AuthMessage['SuccStat'])) {//认证成功 请求启动充电
            if ($AuthMessage['FailReason'] == 0 && $AuthMessage['SuccStat'] == 0) {
                $StartChargeSeq = $this->generate_StartChargeSeq(7);
                ChargeOrderModel::insert(['openid' => $openid, 'area' => $area, 'StartChargeSeq' => $StartChargeSeq]);
                $ChargeModel = 1;
                $StartCharge = $Hard->Get_query_start_charge_test($StartChargeSeq, $ConnectorID, 'QRCode', $ChargeModel, $vin); //ChargeModel vin
                if ($StartCharge['StartChargeSeqStat'] == 1 && $StartCharge['SuccStat'] == 0 && $StartCharge['FailReason'] == 0) {
                    $order_number = $this->generate_order(6);
                    $charge_date = date('Y-m-d', time());
                    $account = 200;
                    $Equipment_number = Db::table('charge_pile')->where('ConnectorID', $ConnectorID)->value('charge_pile_number');
                    $mess = [
                        'order_number' => $order_number,
                        'charge_station_id' => $station_id,
                        'openid' => $openid,
                        'charge_date' => $charge_date,
                        'Equipment_number' => $Equipment_number,
                        'StartChargeSeq' => $StartChargeSeq,
                        'status' => $StartCharge['StartChargeSeqStat'],
                        'StartChargeSeqStat' => $StartCharge['StartChargeSeqStat'],
                        'ConnectorID' => $ConnectorID,
                        'WithholdingMoney' => $account,
                        'EvcInfoNo' => $vin
                    ];
                    Db::table($table_user)->where('openid', $openid)->update(['account' => Db::raw('account-' . $account)]);
                    Db::table($table)->save($mess);
                    $time = date('Y-m-d H:i:s');
                    Log::write('Reservation order has been generated. The Message is ' . $time . json($mess)->getContent());
                    return ['code' => 200, 'message' => '订单创建成功', 'order_number' => $order_number];
                } elseif ($StartCharge['SuccStat'] == 1) {
                    if ($StartCharge['FailReason'] == 0) {
                        return ['code' => 1, 'message' => '设备认证操作结果失败'];
                    } elseif ($StartCharge['FailReason'] == 1) {
                        return ['code' => 1, 'message' => '设备认证操作结果失败 此设备不存在'];
                    } elseif ($StartCharge['FailReason'] == 2) {
                        return ['code' => 1, 'message' => '设备认证操作结果失败 此设备离线'];
                    } elseif ($StartCharge['FailReason'] == 90) {
                        return ['code' => 1, 'message' => '设备认证操作结果失败 车辆未备案'];
                    } elseif ($StartCharge['FailReason'] == 91) {
                        return ['code' => 2, 'message' => '设备认证操作结果失败 该车辆充电中'];
                    } else {
                        return ['code' => 1, 'message' => '未知错误 设备认证成功 请求充电失败'];
                    }
                } else {
                    return ['code' => 1, 'message' => '未知错误 设备认证成功 请求充电失败'];
                }
            } elseif ($AuthMessage['SuccStat'] == 1) {
                if ($AuthMessage['FailReason'] == 0) {
                    return ['code' => 1, 'message' => '设备认证操作结果失败'];
                } elseif ($AuthMessage['FailReason'] == 1) {
                    return ['code' => 1, 'message' => '设备认证操作结果失败 此设备尚未插枪'];
                } elseif ($AuthMessage['FailReason'] == 2) {
                    return ['code' => 1, 'message' => '设备认证操作结果失败 设备检测失败'];
                } else {
                    return ['code' => 1, 'message' => '未知错误 设备认证失败'];
                }
            } else {
                return ['code' => 1, ' message' => '未知错误 设备认证失败'];
            }
        } else {
            return ['code' => 1, 'message' => '未知错误 设备认证失败'];
        }
    }

    public function ModifyAppointmentTime($time): \think\response\Json
    {
        $dateTime = new DateTime($time);
        $formattedTime = $dateTime->format('H:i:s');
        $modify = Db::table('appointment_time')->where('id', 1)->save(['AppointmentTime' => $formattedTime]);
        if ((int)$modify == 1) {
            return json(['code' => 200, 'msg' => '修改成功']);
        } else {
            return json(['code' => 1, 'msg' => '修改失败']);
        }
    }

    public function ShowAppointmentTime(): \think\response\Json
    {
        $AppointmentTime = Db::table('appointment_time')->where('id', 1)->value('AppointmentTime');
        return json(['code' => 200, 'msg' => '显示成功', 'data' => $AppointmentTime]);
    }

    private function generate_StartChargeSeq($length): string
    {
        $chars = '0123456789';
        $time = time();
        $password = config('hard.OperatorID') . $time . '2';//2代表是生成的充电订单号
        for ($i = 0; $i < $length; $i++) {
            $password .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $password;
    }

    private function generate_order($length): string
    {
        $chars = '0123456789';
        $time = time();
        $password = 'DZZS' . $time;
        for ($i = 0; $i < $length; $i++) {
            $password .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $password;
    }

    private function generate_EquipmentAuth($length): string
    {
        $chars = '0123456789';
        $time = time();
        $password = config('hard.OperatorID') . $time . '1';//1代表是生成的设备认证流水号
        for ($i = 0; $i < $length; $i++) {
            $password .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $password;
    }

    public function Encrypt($data): string
    {
        $aes = new Aes();
        return $aes->encrypt($data);
    }

    private function Decrypt($data)
    {
        $aes = new Aes();
        return $aes->decrypt($data);
    }

    private function HmacMD5($data): string
    {
        $md5 = new HMACMD5();
        return $md5->HMAC($data);
    }

    private function CurlSend($url, $data = '', $token = '')
    {
        $data = json_encode($data);
//    echo $data;
        $ch = curl_init();
        $headers = array();
        if ($token) {
            $headers[] = 'Authorization: Bearer ' . $token;
        }
        $headers[] = 'Content-Type: application/json;charset=utf-8';
        $headers[] = 'Content-Length: ' . strlen($data);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        $curl_return = curl_exec($ch);
        curl_close($ch);
        return json_decode(trim($curl_return), true);
    }

    private function checkParams($params)
    {
        $arr = ['OperatorID', 'Data', 'TimeStamp', 'Seq', 'Sig'];
        // 参数检查
        foreach ($arr as $v) {
            if (empty($params[$v])) {
                return $v;
            }
        }
        return 2;
    }

    private function checkSig($OperatorID, $params): int
    {
        $sig = $OperatorID . $params['Data'] . $params['TimeStamp'] . $params['Seq'];
        $ecry_sig = $this->HmacMD5($sig);
        if ($params['Sig'] != $ecry_sig) {
            return 1;
        }
        return 2;
    }


    public function signToken(): string
    {
        $key = config('hard.DataSecretIV');         //这里是自定义的一个随机字串，应该写在config文件中的，解密时也会用，相当    于加密中常用的 盐  salt
        $token = array(
            "iss" => $key,        //签发者 可以为空
            "aud" => '',          //面象的用户，可以为空
            "iat" => time(),      //签发时间
            "nbf" => time() + 1,    //在什么时候jwt开始生效  （这里表示生成100秒后才生效）
            "exp" => time() + 7200, //token 过期时间
            "data" => [           //记录的userid的信息，这里是自已添加上去的，如果有其它信息，可以再添加数组的键值对
                'name' => 'zuxing'
            ]
        );
        //  print_r($token);
        $jwt = JWT::encode($token, $key, "HS256");  //根据参数生成了 token
        return $jwt;
    }


//验证token
    private function checkToken_assess($token): int
    {
        $key = config('hard.DataSecretIV');
        $key = new Key($key, 'HS256');
        $status = array("code" => 2);
        try {
            JWT::$leeway = 60;//当前时间减去60，把时间留点余地
            $decoded = JWT::decode($token, $key, array('HS256')); //HS256方式，这里要和签发的时候对应
            $arr = (array)$decoded;
            $res['code'] = 1;
            $res['data'] = $arr['data'];
            return 2;

        } catch (\Firebase\JWT\SignatureInvalidException $e) { //签名不正确
            $status['msg'] = "签名不正确";
            return 1;
        } catch (\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用
            $status['msg'] = "token失效";
            return 1;
        } catch (\Firebase\JWT\ExpiredException $e) { // token过期
            $status['msg'] = "token失效";
            return 1;
        } catch (\Exception $e) { //其他错误
            $status['msg'] = "未知错误";
            return 1;
        }
    }

    private function encodeData($data, $params, $ret, $msg = ''): \think\response\Json
    {
        $data = json_encode($data);
        $data = $this->Encrypt($data);
        $seq = empty($Seq) ? '0001' : $params['Seq'];
        $datetime = empty($TimeStamp) ? date('YmdHis') : $params['TimeStamp'];
        $sig = $params['OperatorID'] . $data . $datetime . $seq;
        $s = $this->HmacMD5($sig);
        return json(['Msg' => $msg, 'Ret' => $ret, 'Data' => $data, 'Sig' => $s]);
    }

}