<?php
/*
 本代码由 焕岱科技 创建
 技术支持 WX：15285007200
 严禁反编译、逆向等任何形式的侵权行为，违者将追究法律责任
*/

namespace app\common\model\bbfxshop;
use think\Db;
use Ratchet\Client\Connector;
use React\EventLoop\Loop;
use React\Socket\Connector as SocketConnector;
use app\common\model\bbfxshop\AliyunOss;
use app\common\model\bbfxshop\User;

class Shuziren
{

    public static function getSet(){
        global $_W;
        $base = $_W['base'];
        if(empty($base)){
            $base = \app\common\model\bbfxshop\Common::getConfig(true);
        }

        return $base['shuziren'];
    }

    public static function jiesuanNum($order_id){
        $config = self::getSet();
        if($config['status'] != 1){
            return __json(0,"未开启");
        }

        $order = Db::name("bbfx_order")->where(['uniacid'=>UNIACID,'id'=>$order_id,'is_pay'=>1,'refund_status'=>['in',[-1,2]]])->find();
        if(empty($order)){
            return __json(0,"订单信息错误");
        }

        $order_goods = Db::name("bbfx_order_goods")->where(['order_id'=>$order['id']])->select();
        foreach($order_goods as $key => $val){
            $total = max(1,$val['total']);
            $goods = iunserializer($val['goods_info']);
            $plugin = iunserializer($goods['plugin']);
            if($plugin['shuziren']['status'] == 1){ 
                $num = intval($plugin['shuziren']['number']) * $total;
                if($num > 0){
                    $log = Db::name("bbfx_money_log")->where(['uniacid'=>UNIACID,'type'=>14,'item_type'=>61,'item_id'=>$val['id']])->find();
                    if(empty($log)){
                        User::setUserMoney($order['user_id'],$num,14,61,"购买赠送",$val);
                    }
                }
            }
        }

        return __json(1,"操作成功");
    }

    // 生成数字人视频
    public function create($user_id,$title,$text,$video_id,$voice_id,$lang=0){
        $config = self::getSet();

        if($config['status'] != 1){
            return __json(0,'未开启功能');
        }

        if(empty($text)){
            return __json(0,'文字内容不能为空');
        }
        if(mb_strlen($text, "utf8") > 1000){
            return __json(0,'最多1000字内容');
        }

        $user = User::getInfo($user_id);
        if(empty($user)){
            return __json(0,'用户信息错误');
        }

        if($config['use_way'] == 2){
            if($user['shuziren_num'] <= 0){
                return __json(0,'生成数字人次数不足');
            }
        }
        else{
            if(!empty($config['levels'])){
                if(!in_array($user['level'],$config['levels']) || $user['is_distributor'] != 1 || $user['distributor_status'] != 1){
                    return __json(0,'您无权限是用该功能！');
                }
            }
        }
        
        $video_tpl = Db::name("bbfx_shuziren_video_tpl")->where(['id'=>$video_id,'user_id'=>$user_id])->find();
        if(empty($video_tpl) || empty($video_tpl['url'])){
            return __json(0,'视频内容不能为空');
        }

        $voice = Db::name("bbfx_shuziren_voice")->where(['id'=>$voice_id])->where(function($query) use($user_id){
            $query->whereOr(function($query) use($user_id){
                $query->where(['uniacid'=>UNIACID,'user_id'=>$user_id,'type'=>1]);
            });
            $query->whereOr(function($query){
                $query->where(['user_id'=>0,'type'=>0]);
            });
        })->find();
        if(empty($voice)){
            return __json(0,'音色不存在');
        }
        if(empty($voice['voice_id'])){
            return __json(0,'音色ID不能为空');
        }
        if($voice['status'] != 'OK'){
            return __json(0,'音色状态异常');
        }
        if($voice['type'] == 1 && $voice['apikey'] != md5($config['apikey'])){
            return __json(0,'音色不可用');
        }
        
        $video_url = cdnurl($video_tpl['url']);
        // $Voice = new \app\common\model\bbfxshop\ShuzirenVoice();
        // $result = $Voice->createTextVoice($user_id,$voice['voice_id'],$text);
        $result = $this->createTextVoice($user_id,$voice,$text,$lang);
        if($result['code'] != 1){
            return $result;
        }
        // return $result;
        $audio_url = cdnurl($result['data']['url']);

        $url = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/video-synthesis/';
        $header = [
            'Authorization: Bearer '.$config['apikey'],
            'Content-Type: application/json',
            'X-DashScope-Async: enable'
        ];
        $data = [
            "model"=> $config['model'],
            "input"=> [
                "video_url"=> $video_url,
                "audio_url"=> $audio_url,
                "ref_image_url"=> ""
            ],
            "parameters"=> [
                "video_extension"=> true
            ]
        ];

        // file_put_contents("szr222.txt",json_encode($data));

        $res = $this->request($url,'POST',$data,$header);

        // file_put_contents("szr111.txt",json_encode($res));

        if(empty($res["output"]["task_id"])){
            $message = $res['message'];
            if(empty($message)){
                $message = '数字人生成失败';
            }
            return __json(0,$message);
        }

        try{
            Db::startTrans();
            
            $data = [
                'uniacid'=>UNIACID,
                'user_id'=>$user_id,
                'voice_id'=>$voice['id'],
                'video_id'=>$video_tpl['id'],
                'original_video'=>replaceSiteroot($video_url),
                'voice_url'=>replaceSiteroot($audio_url),
                'title'=>$title,
                'text'=>$text,
                'url'=>'',
                'task_id'=>$res["output"]["task_id"],
                'status'=>'PENDING',
                'createtime'=>time()
            ];
            if($lang > 0){
                $languages = $this->getLanguages();
                if(empty($languages[$lang])){
                    $data['lang'] = $languages[$lang]['name'];
                }
            }

            $id = Db::name("bbfx_shuziren_video")->insertGetId($data);
            $data['id'] = $id;

            if($config['use_way'] == 2){
                $res = User::setUserMoney($user['id'],-1,14,62,"生成数字人视频",$data);
                if(empty($res)){
                    throw new \Exception("用户次数不足");
                }
            }

            Db::commit();
        }
        catch(\Exception $e){
            Db::rollback();
            return __json(0,$e->getMessage());
        }

        return __json(1,"视频生成中，请稍后");
    }

    public static function task(){
        $config = self::getSet();
        if($config['status'] != 1){
            return __json(0,'未开启功能');
        }

        $list = Db::name("bbfx_shuziren_video")->where(['uniacid'=>UNIACID,'status'=>['in',['PENDING','PRE-PROCESSING','RUNNING','POST-PROCESSING']]])->select();
        
        if(!empty($list)){

            $instance = new self();
            $fail_reasons = [
                'InvalidParameter'=>'缺少入参，或格式错误',
                'InvalidURL.ConnectionRefused'=>'下载被拒绝，请提供可用的url',
                'InvalidURL.Timeout'=>'下载超时，60s超时',
                'InvalidFile.Size'=>'视频/音频/图像文件必须小于**MB',
                'InvalidFile.Format'=>'文件格式不符合要求，视频支持mp4、avi、mov；音频支持mp3, wav, aac；图片支持jpg, jpeg, png, bmp, webp',
                'InvalidFile.Resolution'=>'视频边长需介于640-2048之间',
                'InvalidFile.FPS'=>'视频帧率需介于15-60fps之间。',
                'InvalidFile.Duration'=>'视频/音频时长需介于2-120s之间',
                'InvalidFile.ImageSize'=>'图片大小超出限制。要求图片长宽比例不大于2，且最长边不大于4096',
                'InvalidFile.Openerror'=>'视频/音频/图像文件无法打开',
                'InvalidFile.Content'=>'输入图片中没有人或有多人',
                'InvalidFile.FaceNotMatch'=>'参考图与视频人脸匹配失败'
            ];
            foreach ($list as $key => $value) {
                $result = $instance->getTaskInfo($value['task_id'],$config['apikey']);
                addFileLog("szrtask111",json_encode($result));
                if($result['data']['task_status'] != $value['status']){
                    $data = ['status'=> $result['data']['task_status']];
                    if($data['status'] == 'SUCCEEDED' || $data['status'] == 'FAILED' || $data['status'] == 'UNKNOWN'){
                        $data['completetime'] = time();
                        if($data['status'] == 'FAILED'){
                            $res_code = $result['data']['code'];
                            $data['fail_reason'] = $fail_reasons[$res_code] ?? '';
                        }
                    }
                    if($data['status'] == 'SUCCEEDED'){
                        $video_url = $result['data']['video_url'];
                        $suffix = substr(strrchr($video_url,'.'),1);
                        $suffix = explode("?",$suffix)[0];
                        $filename = "/ai/video_".$value['id'].".".$suffix;

                        // $videoData = file_get_contents($video_url);
                        // file_put_contents(ROOT_PATH."public".$filename, $videoData);

                        saveFile($video_url,ROOT_PATH."public".$filename);

                        $data["url"] = $filename;

                        $result = AliyunOss::upload($data['url']);
                        if($result['code'] == 1){
                            $data["url"] = $result['data']['url'];
                        }
                    }
                    Db::name("bbfx_shuziren_video")->where(['id'=> $value['id']])->update($data);
                }
            }

        }
        
        return __json(1,'操作成功');
    }

    // 查询数字人视频列表
    public function getVideoList($user_id){
        $config = self::getSet();
        $list = Db::name("bbfx_shuziren_video")->where(['uniacid'=>UNIACID,'user_id'=>$user_id])->order("id desc")->select();

        $status_list = ['PENDING'=>'排队中','PRE-PROCESSING'=>'前置处理中','RUNNING'=>'处理中','POST-PROCESSING'=>'后置处理中','SUCCEEDED'=>'成功','FAILED'=>'失败','UNKNOWN'=>'作业不存在或状态未知'];

        foreach ($list as $key => &$value) {
            $value['url'] = cdnurl($value['url']);
            $value['status_text'] = $status_list[$value['status']];
            $value['createtime'] = date("Y-m-d H:i:s",$value['createtime']);
        }
        unset($value);
        
        return __json(1,'操作成功',['list'=>$list]);
    }

    // 根据文字生成音频文件
    public function createTextVoice($user_id,$voice,$text,$lang = 0){
        // 文档地址：https://help.aliyun.com/zh/model-studio/qwen-tts-api?spm=a2c4g.11186623.0.0.63ba1457MgNUAv#83d3248c25qtq

        $config = self::getSet();
        if($config['status'] != 1){
            return __json(0,"未开启功能");
        }

        $api_key = $config['apikey'];
        $headers = [
            'Authorization: Bearer '.$api_key,
            'Content-Type: application/json'
        ];


        $voice_id = $voice['voice_id'];
        $langs = null;
        if($lang > 0){
            $languages = $this->getLanguages();
            if(!empty($languages[$lang])){
                $langs = $languages[$lang];
            }
        }
        
        if($voice['type'] == 1){
            $url = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/SpeechSynthesizer";
            $mode = "cosyvoice-v2";
            $data = [
            "model"=> $mode,
                "input"=> [
                    "text"=> $text,
                    "voice"=> $voice_id
                ]
            ];

            if(!empty($langs)){
                $data['input']['language_hints'] = [$langs['code2']];
            }
        }
        else{
            $url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation";
            $mode = "qwen3-tts-flash";
            $data = [
            "model"=> $mode,
                "input"=> [
                    "text"=> $text,
                    "voice"=> $voice_id,
                    "language_type"=> 'Auto',
                ]
            ];

            if(!empty($langs)){
                $data['input']['language_type'] = $langs['code1'];
            }
        }
        

        $result = $this->request($url,"POST",$data,$headers);
        if(!empty($result['output']['audio'])){
            $filename = '/ai/voice_'.$user_id.'_'.time().'.mp3';
            $output_file = ROOT_PATH."public".$filename; // 输出文件路径
            // return __json(1,"操作成功",['url'=>'/ai/voice_83_1754639964.mp3']);
            if(!is_dir(ROOT_PATH."public/ai")) {
                mkdir(ROOT_PATH."public/ai");
            }

            if (file_exists($output_file)) {
                // 清空文件内容
                addFileLog($output_file);
            }

            saveFile($result['output']['audio']['url'],ROOT_PATH."public".$filename);

            return __json(1,'操作成功',['url'=>$filename]);
        }
        else{
            $message = $result['message'] ?? '音频生成失败';
            return __json(0,$message);  
        }
    }

    public function initVoiceFile(){
        $list = Db::name("bbfx_shuziren_voice")->where(['uniacid'=>UNIACID,'type'=>0])->field("id,url,voice_id")->select();
        if(!empty($list)){
            foreach ($list as $key => $val) {
                $filename = '/ai/sys_voice_'.$val['voice_id'].'.wav';
                $output_file = ROOT_PATH."public".$filename; // 输出文件路径
                $name = str_replace(" ","",$val['voice_id']);
                $voice_url = "https://baibian1.oss-cn-beijing.aliyuncs.com/shuziren/".$name.".wav";
                if (!file_exists($output_file)) {
                    saveFile($voice_url,$output_file);
                }
                if(empty($val['url'])){
                    Db::name("bbfx_shuziren_voice")->where(['id'=>$val['id']])->update(['url'=>$filename]);
                }
            }
        }
        return __json(1,'操作成功');
    }

    public function getLanguages(){
        $languages = [
            ['name'=>'自动','code1'=>'','code2'=>''],
            ['name'=>'中文','code1'=>'zh','code2'=>'Chinese'],
            ['name'=>'英文','code1'=>'en','code2'=>'English'],
            ['name'=>'德语','code1'=>'de','code2'=>'German'],
            ['name'=>'法语','code1'=>'fr','code2'=>'French'],
            ['name'=>'日语','code1'=>'ja','code2'=>'Japanese'],
            ['name'=>'葡萄牙语','code1'=>'pt','code2'=>'Portuguese'],
            ['name'=>'俄语','code1'=>'ru','code2'=>'Russian']
        ];
        return $languages;
    }
 

    // 创建音色
    public function createVoice($user_id,$voice_url,$title){

        $config = self::getSet();
        if($config['status'] != 1){
            return __json(0,"未开启功能");
        }
        $api_key = $config['apikey'];
        $voice_url = cdnurl($voice_url);
        if(empty($voice_url)){
            return __json(0,"音色文件不能为空");
        }
        $suffix = substr(strrchr($voice_url,'.'),1);
        $suffix = explode("?",$suffix)[0];
        $suffix = strtolower($suffix);
        // if(!strstr($voice_url, '.mp3')){
        if(!in_array($suffix,['mp3','wav','m4a','MP3','WAV','M4A'])){
            return __json(0,"请上传mp3、wav、m4a格式音色文件");
        }
        if(empty($title)){
            return __json(0,"标题不能为空");
        }
        $url = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization";
        $headers = [
            'Authorization: Bearer '.$api_key,
            'Content-Type: application/json'
        ];

        $data = [
            "model"=> "voice-enrollment",
            "input"=> [
                "action"=> "create_voice",
                "target_model"=> "cosyvoice-v2",
                "prefix"=> $suffix,
                "url"=> $voice_url
            ]
        ];

        $result = $this->request($url,"POST",$data,$headers);
        if(!empty($result['output']['voice_id'])){

            $data = [
                'uniacid'=> UNIACID,
                'type'=>1,
                'user_id'=>$user_id,
                'title'=>$title,
                'url'=>replaceSiteroot($voice_url),
                'voice_id'=>$result['output']['voice_id'],
                'apikey'=>md5($api_key),
                'status'=>'DEPLOYING',
                'createtime'=>time()
            ];

            Db::name("bbfx_shuziren_voice")->insert($data);

            return __json(1,"音色创建成功");
        }
        else{
            return __json(0,"音色创建失败");
        }
    }

    // 删除音色
    public function deleteVoice($id,$user_id=0){
        $config = self::getSet();
        $api_key = $config['apikey'];
        $where = ["id"=>$id,'apikey'=>md5($api_key)];
        if(!empty($user_id)){
            $where['user_id'] = $user_id;
        }
        $item = Db::name("bbfx_shuziren_voice")->where($where)->find();
        if(empty($item)){
            return __json(0,'音色数据不存在');
        }

        if($item['type'] != 1){
            return __json(0,'系统音色不可删除');
        }

        $url = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization";
        $headers = [
            'Authorization: Bearer '.$api_key,
            'Content-Type: application/json'
        ];

        $data = [
            "model"=> "voice-enrollment",
            "input"=> [
                "action"=> "delete_voice",
                "voice_id"=> $item['voice_id']
            ]
        ];

        $result = $this->request($url,"POST",$data,$headers);
        if(!empty($result['usage'])){
            
            Db::name("bbfx_shuziren_voice")->where(['id'=>$item['id']])->delete();

            return __json(1,'删除成功');
        }
        else{
            return __json(0,'删除失败');
        }
    }

    // 获取可用音色列表
    public function getVoiceList($user_id){
        $config = self::getSet();
        $api_key = $config['apikey'];
        $all = Db::name("bbfx_shuziren_voice")->where(function($query) use($user_id,$api_key){
            $query->whereOr(function($query) use($user_id,$api_key){
                $query->where(['uniacid'=>UNIACID,'user_id'=>$user_id,'type'=>1,'apikey'=>md5($api_key)]);
            });
            $query->whereOr(function($query){
                $query->where(['user_id'=>0,'type'=>0]);
            });
        })->order("id desc")->select();
        $list = [];
        foreach ($all as $key => $value) {
            $value['url'] = cdnurl($value['url']);
            if($value["status"] == 'OK'){
                // $list[] = $value;
            }
            else if($value['status'] == 'DEPLOYING'){
                $result = $this->queryVoice($value['voice_id'],$api_key);
                if(!empty($result['data']['status'])){
                    Db::name("bbfx_shuziren_voice")->where(['id'=>$value['id']])->update(['status'=>$result['data']['status']]);
                    if($result['data']['status'] == 'OK'){
                        $value['status'] = $result['data']['status'];
                        // $list[] = $value;
                    }
                }
            }
            if($value['type'] == 0){
                $value['title'] .= '（系统音色）';
            }
            $value['createtime'] = date("Y-m-d H:i:s",$value['createtime']);
            $list[] = $value;
        }

        return __json(1,'操作成功',['list'=>$list]);
    }

    // 查询指定音色
    public function queryVoice($voice_id,$api_key){
        $url = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization";
        $headers = [
            'Authorization: Bearer '.$api_key,
            'Content-Type: application/json'
        ];

        $data = [
            "model"=> "voice-enrollment",
            "input"=> [
                "action"=> "query_voice",
                "voice_id"=> $voice_id
            ]
        ];
          
        $result = $this->request($url,"POST",$data,$headers);
        if(!empty($result['output'])){
            return __json(1,'查询成功',['status'=>$result['output']['status']]);
        }
        else{
            return __json(0,'查询失败');
        }
    }

    public function getVoiceTexts($text){

        if(mb_strlen($text, "utf8") <= 1800){
            
            return [$text];
        }

        $fh = ['。','；','！','？','.',';','?','!'];
        $list = mb_str_split($text);
        $num = 0;
        $text = '';
        $texts = [];
        
        foreach ($list as $key => $value) {
            if (!preg_match('/[\x{4e00}-\x{9fa5}]/u', $value)) {
                $ii = 1;
            }
            else{
                $ii = 2;
            }
            $s = $num + $ii;
            if(($s >= 1200 && $s <= 2000 && in_array($value, $fh)) || $key == count($list) -1) {
                $texts[] = $text.$value;
                $text = '';
                $num = 0;
            }
            else{
                $text .= $value;
                $num += $ii;
            }
        }

        return $texts;
    }

    

    public function getTaskInfo($task_id,$apikey){
        $config = self::getSet();
        $url = "https://dashscope.aliyuncs.com/api/v1/tasks/{$task_id}";
        $header = [
            'Authorization: Bearer Bearer '.$apikey,
            'Content-Type: application/json'
        ];
        $res = $this->request($url,'GET','',$header);
        

        $status = ['PENDING'=>'排队中','PRE-PROCESSING'=>'前置处理中','RUNNING'=>'处理中','POST-PROCESSING'=>'后置处理中','SUCCEEDED'=>'成功','FAILED'=>'失败','UNKNOWN'=>'作业不存在或状态未知'];

        // if(empty($res['output']['video_url'])){
        //     $message = $res['message'];
        //     if(empty($message)){
        //         $message = '数据查询失败';
        //         if($res['output']['task_status']){
        //             $message = $status[$res['output']['task_status']];
        //         }
        //     }
        //     return __json(0,$message);
        // }

        // echo "<pre>";
        // print_r($res);die; 
        
        return __json(1,'操作成功',$res['output']);
    }
    
    public function request($url,$method,$data,$headers){
        $ch = curl_init();
        // 设置cURL选项
        curl_setopt($ch, CURLOPT_URL, $url);
        if($method == "POST"){
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        // 执行cURL会话
        $response = curl_exec($ch);
        // 检查是否有错误发生
        if (curl_errno($ch)) {
            echo 'Curl error: ' . curl_error($ch);
        }
        // 关闭cURL资源
        curl_close($ch);
        if(is_string($response) && !empty($response)){
            $response1 = json_decode($response,true);
            if(!empty($response1)){
                $response = $response1;
            }
        }

        return $response;
    }
}