<?php
// +----------------------------------------------------------------------
// | likeadmin快速开发前后端分离管理后台(PHP版)
// +----------------------------------------------------------------------
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
// | 开源版本可自由商用,可去除界面版权logo
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
// | github下载:https://github.com/likeshop-github/likeadmin
// | 访问官网:https://www.likeadmin.cn
// | likeadmin团队 版权所有 拥有最终解释权
// +----------------------------------------------------------------------
// | author: likeadminTeam
// +----------------------------------------------------------------------

namespace app\api\controller;

use IFlytek\Xfyun\Speech\ChatClient;
use IFlytek\Xfyun\Speech\IatClient;
use IFlytek\Xfyun\Speech\TtsClient;
use IFlytek\Xfyun\Speech\OcrClient;
use think\facade\Db;
use WebSocket\Client;
use GuzzleHttp\Client as GzClient;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\GuzzleException;
use Guzzle\Http\Exception\RequestException;

/**
 * 讯飞
 * Class WechatController
 */
class XunFeiController extends BaseApiController
{
    public array $notNeedLogin = ['chat', 'iat', 'tts', 'ocr', 'iatWss', 'analyse'];

    private $app_id='2eda6c2e';
    private $api_key='12ec1f9d113932575fc4b114a2f60ffd';
    private $api_secret='MDEyMzE5YTc5YmQ5NjMwOTU1MWY4N2Y2';

    //星火认知chat
    public function chat()
    {
        header('X-Accel-Buffering: no');
        $content = $this->request->param('content');
        if(empty($content)){
            return $this->data(['answer' => '']);
        }
        $chat=new ChatClient($this->app_id,$this->api_key,$this->api_secret);
        $client = new Client($chat->assembleAuthUrl('wss://spark-api.xf-yun.com/v2.1/chat'));
        // 连接到 WebSocket 服务器
        if ($client) {
            $header = [
                "app_id" => $this->app_id,
                "uid"    => "1"
            ];
            $parameter = [
                "chat" => [
                    "domain"        => "generalv2",
                    "temperature"   => 0.5,
                    "max_tokens"    => 1024
                ]
            ];
            $payload = [
                "message"  => [
                    "text" => [
                        ["role" => "user", "content" => $content]
                    ]
                ]
            ];
            $data = json_encode([
                "header"    => $header,
                "parameter" => $parameter,
                "payload"   => $payload
            ]);

            $client->send($data);
            $answer = "";
            while(true){
                $response = $client->receive();
                $resp = json_decode($response, true);
                $code = $resp["header"]["code"] ?? 0;
                if(0 == $code){
                    $status = $resp["header"]["status"];
                    if($status != 2){
                        $content = $resp['payload']['choices']['text'][0]['content'];
                        $answer .= $content;
                    }else{
                        $content = $resp['payload']['choices']['text'][0]['content'];
                        $answer .= $content;
                        break;
                    }
                }else{
                    return $this->fail( "服务返回报错 " . $response);
                    break;
                }
            }
            return $this->data(['answer' => $answer]);
        } else {
            return $this->fail('无法连接到 WebSocket 服务器');
        }
    }

    //AI分析信息
    public function analyse()
    {
        $informationg_demand_id = $this->request->param('informationg_demand_id');
        if(empty($informationg_demand_id)){
            return $this->fail('信息参数错误');
        }
        $informationg = Db::name('user_informationg_demand')->where('id', $informationg_demand_id)->where('status', 1)->find();
        $type_name = Db::name('category_business')->where('id', $informationg['category_child'])->value('name');
        $data_field = json_decode($informationg['data_field'], true);
        $demand = '';
        foreach($data_field as $k=>$v) {
            $demand .= $k . ':' . $v . ';';
        }
        $question = "根据以下{$type_name}信息【{$demand}】请问有那些商机?";
        $chat=new ChatClient($this->app_id,$this->api_key,$this->api_secret);
        $client = new Client($chat->assembleAuthUrl('wss://spark-api.xf-yun.com/v2.1/chat'));
        // 连接到 WebSocket 服务器
        if ($client) {
            $header = [
                "app_id" => $this->app_id,
                "uid"    => "1"
            ];
            $parameter = [
                "chat" => [
                    "domain"        => "generalv2",
                    "temperature"   => 0.5,
                    "max_tokens"    => 1024
                ]
            ];
            $payload = [
                "message"  => [
                    "text" => [
                        ["role" => "user", "content" => $question]
                    ]
                ]
            ];
            $data = json_encode([
                "header"    => $header,
                "parameter" => $parameter,
                "payload"   => $payload
            ]);

            $client->send($data);
            $answer = '';
            while(true){
                $response = $client->receive();
                $resp = json_decode($response, true);
                $code = $resp["header"]["code"] ?? 0;
                if($code == 0){
                    $status  = $resp["header"]["status"];
                    $content = $resp['payload']['choices']['text'][0]['content'] ?? '';
                    $answer .= $content;
                    if($status == 2){
                        break;
                    }
                }else{
                    return $this->fail( "服务返回报错 " . $response);
                    break;
                }
            }
            $data = [
                'ai_aianalyse' => $answer,
                'update_time' => time(),
            ];
            $res = Db::name('user_informationg_demand')->where('id', $informationg_demand_id)->update($data);
            if (!$res) {
                return $this->fail('AI分析信息失败');
            }
        } else {
            return $this->fail('无法连接到 WebSocket 服务器');
        }
        return $this->data(['question'=>$question, 'answer' => $answer]);
    }

    //语音听写(流式版)
    public function iat()
    {
        header('X-Accel-Buffering: no');
        $file = request()->file('audio');
        if (empty($file)) {
            return $this->fail('未上传音频文件');
        }
        // 上传音频临时文件
        $savename = \think\facade\Filesystem::putFile('audio', $file);
        
        $file = app()->getRootPath() . '/runtime/storage/' . $savename;
        if (!file_exists($file)) {
            return $this->fail('未上传音频文件');
        }
        $filesize = filesize($file);
        if ($filesize > 1 * 1024 * 1024) {
            return $this->fail('录音文件太长');
        }
        $last_file = substr($savename, -36);
        $copyFile = app()->getRootPath() . '/public/uploads/iat/' . $last_file;

        // ********** 临时方案 ********** //
        copy($file, $copyFile);
        // $last_file = 'a1fcdd96c7967b48add17b52ab456368.mp3';
        $curl_url = "https://dev.app.tword.cn/ffmpeg.php?file={$last_file}";
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $curl_url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_exec($ch);
        curl_close($ch);

        $file = "https://dev.app.tword.cn/iat/" . $last_file . ".pcm";
        // $ext = pathinfo($file, PATHINFO_EXTENSION);
        $ext = "pcm";
        $extArray = ['mp3', 'pcm'];
        if (!in_array($ext, $extArray)) {
            return $this->fail('录音格式错误');
        }
        // ********** 临时方案 ********** //
        $encoding = 'raw';
        if ($ext == 'mp3') {
            $encoding = 'lame';
        }
        if ($ext == 'pcm') {
            $encoding = 'raw';
        }
        $audioFile = fopen($file, 'rb');
        if ($audioFile === false) {
            return $this->fail('音频文件异常');
        }

        try {
            $words = '';
            $iatHostUrl = "wss://iat-api.xfyun.cn/v2/iat";
            $iat = new IatClient($this->app_id, $this->api_key, $this->api_secret);
            $client = new Client($iat->assembleAuthUrl($iatHostUrl));
            $frameSize = 1280; //每一帧的音频大小
            $intervel = 20 * 1000; //发送音频间隔
            $status = 0;
            while (true) {
                $len = fread($audioFile, $frameSize);
                if ($len === false) {
                    break;
                }
                if ($len === '') { //文件读取完了
                    $status = 2;
                }
                switch ($status) {
                    case 0: //发送第一帧音频,带business 参数
                        $frameData = array(
                            'common' => array(
                                'app_id' => $this->app_id //appid 必须带上,只需第一帧发送
                            ),
                            'business' => array( //business 参数,只需一帧发送
                                'language' => 'zh_cn',
                                'domain' => 'iat',
                                'accent' => 'mandarin'
                            ),
                            'data' => array(
                                'status' => 0,
                                'format' => 'audio/L16;rate=16000',
                                'audio' => base64_encode($len),
                                'encoding' => $encoding
                            )
                        );
                        $client->send(json_encode($frameData));
                        $status = 1;
                        break;
                    case 1:
                        $frameData = array(
                            'data' => array(
                                'status' => 1,
                                'format' => 'audio/L16;rate=16000',
                                'audio' => base64_encode($len),
                                'encoding' => $encoding
                            )
                        );
                        $client->send(json_encode($frameData));
                        break;
                    case 2:
                        $frameData = array(
                            'data' => array(
                                'status' => 2,
                                'format' => 'audio/L16;rate=16000',
                                'audio' => base64_encode($len),
                                'encoding' => $encoding
                            )
                        );
                        $client->send(json_encode($frameData));
                        break 2;
                }
                //模拟音频采样间隔
                usleep($intervel);
            }
            while (true) {
                $response = $client->receive();
                if ($response === null) {
                    break;
                }
                $resp = json_decode($response, true);
                $code = $resp['code'];
                if ($code != 0) {
                    break;
                }
                $message = $resp['message'];
                $data = $resp['data'];
                $result = $data['result'];
                $status = $data['status'];
                foreach($result['ws'] as $v) {
                    $words .= $v['cw'][0]['w'] ?? '';
                }
                if ($status === 2) {
                    break;
                }
            }
            fclose($audioFile);
            // 删除临时音频文件
            if (file_exists($file)) {
                unlink($file);
            }
        } catch (\Exception $e) {
            if (file_exists($file)) {
                // 删除临时文件
                unlink($file);
            }
            return $this->fail($e->getMessage());
        }
        return $this->data(['words' => $words]);
    }

    //获取语音听写(流式版)websocket地址
    public function iatWss()
    {
        header('X-Accel-Buffering: no');
        $iatHostUrl = "wss://iat-api.xfyun.cn/v2/iat";
        $iat = new IatClient($this->app_id, $this->api_key, $this->api_secret);
        $iat_wss = $iat->assembleAuthUrl($iatHostUrl);
        //后期添加鉴权
        return $this->data(['iat_wss' => $iat_wss]);
    }

    //语音合成(流式版)
    public function tts() 
    {
        header('X-Accel-Buffering: no');
        $ttsHostUrl = "wss://tts-api.xfyun.cn/v2/tts";
        $text = request()->param('text');
        if (empty($text)) {
            return $this->fail('未上传文本参数');
        }
        $file_name = date('YmdHis', time()) . mt_rand(1000, 9999) . '.mp3';
        $date_path = date('Ymd');
        $dir = app()->getRootPath() . '/public/uploads/audio/' . $date_path;
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
        $audioFile = $dir . '/' . $file_name;
        $business = [
            'aue'    => 'lame', //mp3格式
            'vcn'    => 'aisjinger', //发音人
            'auf'    => 'audio/L16;rate=16000', //音频采样率
            'speed'  => 50, //语速
            'volume' => 100, //音量
            'pitch'  => 50, //音高
            'tte'    => 'UTF8'
        ];
        try {
            $tts = new TtsClient($this->app_id, $this->api_key, $this->api_secret, $business);
            file_put_contents($audioFile, $tts->request($text)->getBody()->getContents());
            //生成语音文件需要定时清理
        } catch (\Exception $e) {
            return $this->fail($e->getMessage());
        }
        return $this->data(['audio_file' => request()->domain() . '/uploads/audio/' . $date_path . '/' . $file_name]);
    }

    //通用文字识别
    public function ocr() 
    {
        $ocrHostUrl = "https://api.xf-yun.com/v1/private/sf8e6aca1";
        $file = request()->param('image');
        if (empty($file)) {
            return $this->fail('未上传图片文件');
        }
        /*
        // 上传图片临时文件
        $savename = \think\facade\Filesystem::putFile('ocr', $file);
        $file = app()->getRootPath() . '/runtime/storage/' . $savename;
        if (!file_exists($file)) {
            return $this->fail('未上传图片文件');
        }
        $filesize = filesize($file);
        if ($filesize > 4 * 1024 * 1024) {
            return $this->fail('图片文件不能超过4M');
        }
        */
        $ext = pathinfo($file, PATHINFO_EXTENSION);
        $base64_image = base64_encode(file_get_contents($file));
        $ocr = new OcrClient($this->app_id, $this->api_key, $this->api_secret);
        $ocrHostUrl = $ocr->assembleAuthUrl($ocrHostUrl);
        $requestBody = [
            'header' => [
                'app_id' => $this->app_id,
                'status' => 3
            ],
            'parameter' => [
                'sf8e6aca1' => [
                    'category' => 'ch_en_public_cloud',
                    'result'   => [
                        'encoding' => 'utf8',
                        'compress' => 'raw',
                        'format'   => 'json'
                    ]
                ]
            ],
            'payload' => [
                'sf8e6aca1_data_1'   => [
                    'encoding' => $ext,
                    'status'   => 3,
                    'image'    => $base64_image
                ]
            ]
        ];
        $text = [];
        try {
            $client = new GzClient(['timeout' => 5]); 
            $response = $client->request('POST', $ocrHostUrl, [
                'json'   => $requestBody,
                'verify' => false
            ]);
            $responseData = $response->getBody()->getContents();
            $responseArray = json_decode($responseData, true);
            if (empty($responseArray['payload']['result']['text'])) {
                return $this->fail('json解析错误');
            }
            $encodeText = $responseArray['payload']['result']['text'];
            $textArray = json_decode(base64_decode($encodeText), true);
            $lineArray = $textArray['pages'][0]['lines'] ?? [];
            foreach($lineArray as $item) {
                $content = $item['words'][0]['content'] ?? '';
                if ($content) {
                    $text[] = $content;
                }
            }
        } catch (GuzzleException $e) {
            return $this->fail($e->getMessage());
        }
        return $this->data(['words' => $text]);
    }

}