<?php

namespace app\server;

use app\model\Orders;
use Exception;
use support\Log;
use support\Redis;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;

class Douyin
{

    private $cookie = '';
    private $id = '';
    private $token = '';
    private $gate = 'https://life.douyin.com';
    public $totalPage = 10;

    public function __construct()
    {
        $this->cookie = $this->_cookie();
        $this->id = $this->_id();
        $this->token = $this->_token();
    }

    public function login()
    {

    }

    public function voucher($check_sn)
    {

        //https://lvyou.meituan.com/nib/trade/b/voucher/show?voucherCode=130949862550187&yodaReady=h5&csecplatform=4&csecversion=2.4.0

        //https://lvyou.meituan.com/nib/trade/b/voucher/show?voucherCode=130949862550187&yodaReady=h5&csecplatform=4&csecversion=2.4.0
        $res = $this->_curl('/nib/trade/b/voucher/show', [
            'voucherCode' => $check_sn,
            'yodaReady' => 'h5',
            'csecplatform' => '4',
            'csecversion' => '2.4.0'
        ]);

        return $res;
    }

    /*public function get($page, $start = null, $end = null, $orderId = '')
    {

        if (empty($start) || empty($end)) {
            $start = date('Y-m-d 00:00:00');
            $end = date('Y-m-d 23:59:59');
        }

        $start = strtotime($start) * 1000;
        $end = strtotime($end) * 1000 + 999;

        if ($start < strtotime('2024-05-23') * 1000) {
            $this->totalPage = 1;
            return [];
        }

        $_list = [];
        foreach (array_keys(Orders::DouyinStatus) as $status) {

            $params = [
                'page_index' => $page,
                'page_size' => 20,
                'status' => $status,
                'order_id' => '',
                'sku_name' => '',
                'last_phone' => '',
                'category' => [
                    "third_category_list" => []
                ]
            ];

            $list = $this->_curl('/life/trip/v2/travel_agency/book/search?root_life_account_id='.$this->_id(), $params, 'POST');
            if (empty($list) || $list->status_code != 0) {
                throw new \Exception("抖音拉单失败,Err:" . json_encode($list));
            }

            if ($list && $list->status_code == 0 && !empty($list->order_list)) {
                foreach ($list->order_list as $order) {
                    echo "抖音 订单号：{$order->order_info->order_id} \n\n";
                    if (empty($cert)) {
                        $cert = $this->_cert($order->order_info->order_id);
                        if (empty($cert)) {
                            throw new \Exception("抖音拉单{$order->order_info->order_id}详情获取失败");
                        }
                    }

                    $item = new Orders();
                    $item->os = 3;
                    $item->sn = $order->order_info->order_id;
                    $item->product_id = $order->order_info->product_id;
                    $item->product_name = $order->order_info->product_name;
                    $item->category_id = $order->order_info->product_sub_type;
                    $item->create_at = $order->order_info->pay_time * 1000;
                    $item->mobile = $order->order_info->buyer_phone;
                    $item->travel_date = $order->book_info->book_start_date ?? null;
                    $item->unit_price = $order->order_info->pay_amount;
                    $item->total_price = $order->order_info->pay_amount;
                    $item->actual_price = $order->order_info->pay_amount;
                    $item->quantity = $order->book_child_info->rec_person_num;
                    $item->order_status = $status;
                    $item->asset_status = $cert->status ?? 0;
                    $item->category_desc = $order->order_info->sku_category_name;
                    $item->asset_price = $cert->status == 2 || $cert->status == 5 ? $cert->amount->verify_amount : 0;
                    //抖音的核销金额需要查核销订单

                    if ($item->create_at < strtotime('2024-05-23') * 1000) {
                        $this->totalPage = 1;
                        break;
                    }

                    //if(mb_strrpos($item->product_name, "S") === false) continue;
                    if (!(mb_strrpos($item->product_name, "S") || mb_strrpos($item->product_name, "G"))) continue;

                    $_list[] = $item;
                }
            }
        }

        return $_list; //返回半成品
    }*/

    /**
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     * @throws DbException
     * @throws Exception
     */
    public function get($page, $start = null, $end = null, $orderId = '')
    {
        if (empty($start) || empty($end)) {
            $start = date('Y-m-d 00:00:00');
            $end = date('Y-m-d 23:59:59');
        }

        $start = strtotime($start);
        $end = strtotime($end);

        if ($start < strtotime('2024-05-22')) {
            $this->totalPage = 1;
            return [];
        }

        if ($orderId) {
            $start = $end = null;
        }

        $_list = [];
        $list = $this->_certificateList($start, $end, $page, $orderId);
        if (empty($list->data->list) || $list->status_code !== 0) {
            $this->totalPage = 1;
            return $_list;
        }
        echo date('Y-m-d', $start) . '--' . date('Y-m-d', $end) . " 抖音 page:{$page} total_count:" . $list->data->pagination->total_count . " page_count:" . $list->data->pagination->page_count . " \n\n";

        foreach ($list->data->list as $order) {
            echo "抖音 订单号：{$order->order_id} \n\n";

            $orderDetail = $this->_orderDetail($order->order_id, $order->status);
            if (empty($orderDetail)) {
                throw new Exception("抖音拉单{$order->order_id}详情获取失败");
            }


            $item = new Orders();
            $item->os = 3;
            $item->sn = $order->order_id;
            $item->product_id = $order->sku->sku_id;
            $item->product_name = $order->sku->title;
            $item->category_id = $order->product_sub_type;
            $item->create_at = $order->pay_time * 1000;
            $item->mobile = $orderDetail->order_info->buyer_phone ?? null;
            $item->travel_date = $orderDetail->book_info->book_start_date ?? null;
            $item->unit_price = $order->amount->pay_amount;
            $item->total_price = $order->amount->pay_amount;
            $item->actual_price = $order->amount->pay_amount;
            $item->quantity = $orderDetail->book_child_info->rec_person_num ?? null;
            $item->order_status = $order->status;
            $item->asset_status = $order->status ?? 0;
            $item->category_desc = $order->sku->title;
            $item->asset_price = $order->amount->verify_amount ?? 0;
            //抖音的核销金额需要查核销订单

            if ($item->create_at < strtotime('2024-05-22') * 1000) {
                $this->totalPage = 1;
                break;
            }

            $this->totalPage = intval($list->data->pagination->total_count / $list->data->pagination->page_count);
            if (($list->data->pagination->total_count % $list->data->pagination->page_count) > 0) {
                $this->totalPage += 1;
            }

            //if(mb_strrpos($item->product_name, "S") === false) continue; G 达人 S 自己
            if (!(mb_strrpos($item->product_name, "S") || mb_strrpos($item->product_name, "G") || mb_strrpos($item->product_name, "甄"))) continue;

            $_list[] = $item;
        }

        return $_list; //返回半成品
    }

    /**
     * @throws Exception
     */
    public function _certificateList($start, $end, $page, $orderId)
    {
        $params = [
            "filter" => [
                "start_time" => $start,//开始时间
                "end_time" => $end,//结束时间 时间范围限定一个月
                "is_market" => false,//必带
                /*"status" => 1,//不传 全部,1未核销 2已核销 3申请退款中 4已退款 5部分核销
                "product_option" => [],// 1团购/代金券 13预售券 15次卡 18通兑券/品 26有价券包 1303日历票 1305日历房 1306演出预订 1309日历品 2201团购|线上约
                "is_combo_product" => true,//是否组合品
                "order_id" => "1061241582949143669",//订单号
                "sku_id" => "7379541565490675752" //产品ID*/
            ]
        ];
        if ($orderId) {
            $params['filter']['order_id'] = $orderId;
        }

//        Log::error('===_certificateList查询page='.$page.'时间：'.date('Y-m-d H:i:s',$start).'--'.date('Y-m-d H:i:s',$end).'====');
        $this->_getRetriesLock();

        $list = $this->_curl("/life/trip/fulfilment/v1/query/certificate_list?end_time={$end}&page_index={$page}&page_size=20&start_time={$start}&root_life_account_id=" . $this->_id(), $params, 'POST');

        if (empty($list) || $list->status_code != 0) {
            $this->_getRetriesLock();
            $list = $this->_curl("/life/trip/fulfilment/v1/query/certificate_list?end_time={$end}&page_index={$page}&page_size=20&start_time={$start}&root_life_account_id=" . $this->_id(), $params, 'POST');
            if (empty($list) || $list->status_code != 0) {
                Log::error('===查询时间：' . $start . '--' . $end . '====certificate_list: ' . json_encode($list));
                throw new Exception("抖音拉单失败,Err:" . json_encode($list));
            }
        }
//        Log::warning('====list=====',['list'=>$list->data->pagination]);
        return $list;
    }

    public function _getRetriesLock(): bool
    {
        /*$pattern = 'php webman spider:mt';
        $maxProcesses = 24;
        $this->_killMiddleProcesses($pattern, $maxProcesses);*/


        $maxRetries = 20;
        $retries = 0;
        while ($retries < $maxRetries) {
            $back = Redis::set('SpiderMt:CertificateList:lock', time(), 'EX', 1, 'NX');
            if ($back) {
                break;
            }
            $retries++;
            sleep(1);
        }
        return true;
    }

    public function _killMiddleProcesses($pattern, $maxProcesses)
    {
        // 检查操作系统是否为 Linux
        if (PHP_OS !== 'Linux') {
            echo "当前系统不是 Linux，跳过执行。\n";
            return false;
        }

        // 获取当前进程的 PID
        $currentPid = getmypid();

        // 获取进程列表
        $command = "ps aux | grep '$pattern' | grep -v grep | grep -v '$currentPid' | awk '{print $2,$10}'";
        $output = [];
        exec($command, $output);

        $processCount = count($output);

        // 如果进程数量超过最大值，则终止中间部分的 x-12 个进程
        if ($processCount > $maxProcesses) {
            $processesToKill = $processCount - $maxProcesses;
            $processesKilled = 0;

            // 找到运行时间最短的不包含自己的进程并终止它
            foreach ($output as $line) {
                list($pid, $runtime) = explode(' ', $line);
                $runtimeParts = explode(':', $runtime);
                $hours = intval($runtimeParts[0]);
                $minutes = intval($runtimeParts[1]);
                $totalMinutes = $hours * 60 + $minutes;

                if ($pid !== $currentPid) {
                    exec("kill -9 $pid");
                    $processesKilled++;

                    echo "Killed process with PID: $pid\n";

                    if ($processesKilled >= $processesToKill) {
                        break;
                    }
                }
            }

            if ($processesKilled === 0) {
                echo "No processes to kill.\n";
            }
        } else {
            echo "Process count is not over the limit.\n";
        }
        return true;
    }

    public function _orderDetail($orderId = null, $status = null)
    {
        $orderDetail = $this->_curl("/life/trip/v2/travel_agency/book/detail", [
            'order_id' => $orderId,
            'book_order_id' => '',
            'status' => $status,
            'root_life_account_id' => $this->_id()
        ]);
        return $orderDetail->data ?? null;
    }

    public function _orderBookingManagement(string $orderId)
    {
        if (empty($orderId)) return [];
        foreach (array_keys(Orders::DouyinReservationStatus) as $status) {
            $params = [
                'page_index' => 1,
                'page_size' => 20,
                'status' => $status,
                'order_id' => $orderId,
                'sku_name' => '',
                'last_phone' => '',
                'category' => [
                    "third_category_list" => []
                ]
            ];

            $orderBookingManagement = $this->_curl('/life/trip/v2/travel_agency/book/search?root_life_account_id=' . $this->_id(), $params, 'POST');
            if (!empty($orderBookingManagement->order_list)) break;
        }
        return $orderBookingManagement->order_list ?? null;
    }

    public function _cert($order_id = null)
    {
        $info = $this->_curl('/life/trip/fulfilment/v1/query/certificate_list?root_life_account_id=' . $this->_id(), [
            'filter' => [
                "start_time" => strtotime(date('Y-m-d 00:00:00', time() - 5 * 24 * 3600)),
                "end_time" => strtotime(date('Y-m-d 23:59:59')),
                "is_market" => false,
                "order_id" => $order_id
            ]
        ], 'POST');

        if ($info->status_code != 0 || empty($info->data->list)) return [];
        $i = $info->data->list[0];
        if (empty($i)) return [];

        $more = $this->_curl('/life/trip/fulfilment/v1/query/certificate_by_id/',
            [
                'certificate_id' => $i->certificate_id,
                'root_life_account_id' => $this->_id()
            ]
        );
        if ($more->status_code == 0) return $more->data->certificate ?? [];
        return [];
    }

    public function _cookie($data = '')
    {
        if ($data) Redis::set('Douyin:cookie', $data);
        return Redis::get('Douyin:cookie');
    }

    public function _id($id = '')
    {
        if ($id) Redis::set('Douyin:id', $id);
        return Redis::get('Douyin:id');
    }

    protected $_try = 0;

    public function _token($token = '')
    {
        if ($token) Redis::set('Douyin:token', $token, 'ex', 3600 * 24 - 50);
        $token = Redis::get('Douyin:token');
        if (empty($token) && $this->_cookie()) {
            if ($this->_try > 3) {
                throw new Exception("cookie 失效");
            }
            $id = $this->_curl('/life/gate/v1/user/detail', null, 'HEAD');
            $this->_try++;
            if ($id) {
                Redis::set('Douyin:token', $id, 'ex', 3600 * 24 - 50);
            }
        }
        return Redis::get('Douyin:token');
    }

    public function _curl($url, $params, $method = 'GET')
    {

        $http = $this->gate . $url;
        if ($method == 'GET') {
            $http = $http . '?' . http_build_query($params);
        }

        $ch = curl_init($http);

        $header = [
            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
            'Sec-Ch-Ua-Platform: "Windows"',
            'Host: life.douyin.com',
            'Sec-Ch-Ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"',
            'Accept-Language: zh-CN,zh;q=0.9',
            'Content-type: application/json'
        ];
        if ($method == 'GET' || $method == 'POST') {
            $header[] = 'x-secsdk-csrf-token: ' . $this->_token();
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        } else {
            curl_setopt($ch, CURLOPT_HEADER, 1);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            $header[] = 'x-secsdk-csrf-request: 1';
        }
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_COOKIE, $this->_cookie());
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        if ($method == 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
        }

        $body = curl_exec($ch);
        curl_close($ch);
        if (!in_array($method, ['GET', 'POST'])) {
            $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
            $header = substr($body, 0, $header_size);
            $in = explode("\n", $body);
            foreach ($in as $i) {
                if (stripos($i, 'x-ware-csrf-token') !== false) {
                    $inn = explode(',', $i);
                    return $inn[1];
                }
            }
            return null;
        }
        return json_decode($body);
    }
}