<?php
declare (strict_types = 1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\facade\Db;

class DataMigration extends Command
{
    protected $println;
    private function println($msg) {
        ($this->println)($msg);
    }

    protected function configure()
    {
        // 指令配置
        $this->setName('migrate')
            ->setDescription('执行数据迁移');
    }

    protected function execute(Input $input, Output $output)
    {
        // 指令输出
        $this->println = function ($msg) use ($output) {
            $output->writeln($msg);   
        };

        try {
            // 迁移tock产品分类
            // $this->productTcoCategory();

            // 迁移产品分类
            // $this->productCategory();

            // 迁移文章
            // $this->migrateArticle([
            //     1 => 2,
            //     2 => 3,
            //     33 => 4,
            //     36 => 5,
            //     16 => 7,
            //     31 => 8,
            //     32 => 9
            // ]);

            // 迁移faq
            // $this->migrateFaq();

            // 迁移附件下载
            // $this->migrateAttachment([
            //     1 => 1,
            //     2 => 2,
            //     3 => 3,
            //     4 => 4,
            //     5 => 5,
            //     43 => 6,
            //     22 => 8,
            //     24 => 11,
            //     25 => 12,
            //     26 => 13,
            //     44 => 9,
            //     45 => 10
            // ]);

            // 迁移视频分类
            // $this->migrateVideoCategory();

            // 迁移视频
            $this->migrateVideo([
                0 => 0, 
                1 => 1, 
                2 => 2, 
                3 => 3, 
                4 => 4, 
                5 => 5, 
                6 => 6, 
                7 => 7, 
                8 => 8, 
                11 => 0, 
                36 => 9, 
                37 => 10, 
                38 => 11, 
                39 => 12, 
                40 => 13, 
                41 => 14, 
                42 => 15, 
                43 => 16, 
                60 => 17, 
                62 => 18, 
                63 => 19
            ]);

            $output->writeln('success');
        } catch(\Throwable $th) {
            $output->writeln($th->getMessage() .':' . $th->getLine());
        }
    }

    // 迁移tco产品分类
    private function productTcoCategory()
    {
        $category = Db::connect('old')
            ->name('product_tco_category')
            ->select();

        foreach ($category as $val) {
            $item = [
                'id'          => $val['id'],
                'language_id' => $val['country_code'] == 'ZH' ? 1 : 2,
                'name'        => $val['name'],
                'tco_id'      => $val['tco_id'],
                'tco_pid'     => $val['tco_pid'],
                'tco_path'    => $val['tco_path'],
                'erp_id'      => $val['erp_id'],
                'erp_pid'     => $val['erp_pid'],
                'erp_path'    => $val['erp_path'],
                'erp_code'    => $val['erp_code'],
                'disabled'    => $val['disabled'],
                'sync_time'   => $val['sync_time'],
            ];
            Db::name('product_tco_category')->insert($item);
        }
    }

    // 迁移产品分类
    private function productCategory()
    {
        $tco_category = Db::connect('old')
            ->name('product_tco_category')
            ->select();
        $tco_category_map = [];
        foreach ($tco_category as $val) {
            $key = sprintf("%s_%s", $val['category_id'], $val['country_code']);
            if (isset($tco_category_map[$key])) {
                $tco_category_map[$key] = [];
            }
            $tco_category_map[$key][] = $val['id'];
        }

        $category = Db::connect('old')
            ->name('product_category')
            ->select()
            ->toArray();

        // 处理数据
        $this->handlerProductCategory(array_to_tree($category, 0, 'pid', 1), $tco_category_map);
    }
    private function handlerProductCategory($category, $map) {
        foreach ($category as $val) {
            $key = sprintf("%s_%s", $val['id'], $val['country_code']);
            $item = [
                'id'                   => $val['id'],
                'language_id'          => $val['country_code'] == 'ZH' ? 1 : 2,
                'unique_id'            => uniqid('PRO_CATE_'),
                'pid'                  => $val['pid'],
                'name'                 => $val['name'],
                'icon'                 => $val['icon'],
                'desc'                 => $val['description'],
                'related_tco_category' => isset($map[$key]) ? implode(',', $map[$key]) : '',
                'sort'                 => $val['sort'] == 9999 ? 0 : $val['sort'],
                'level'                => $val['level'],
                'is_show'              => $val['isshow'],
            ];
            Db::name('product_category')->insert($item);
            if (isset($val['children'])) {
                $this->handlerProductCategory($val['children'], $map);
            }
        }
    }

    // 迁移文章
    private function migrateArticle($category_map = [])
    {
        $this->println('开始迁移文章......');

        if (empty($category_map)) {
            throw new \Exception('请确认分类ID');
        }

        $article = Db::connect('old')
            ->name('article')
            ->where('cid', 'in', array_keys($category_map))
            ->order(['id' => 'asc'])
            ->cursor();

        $uploadMgr = new UploadMannager();
        foreach ($article as $v) {
            // 处理封面图片
            $image = '';
            $ret = $uploadMgr->upload($uploadMgr->download($v['picture']), 'image', 'article');
            if ($ret['code'] == 0) {
                $image = $ret['data']['path'];
            } else {
                $image = $ret['msg'];
            }

            // 处理详情中图片
            $content = $v['content'];
            preg_match_all('/<img.*?src=[\'"](.*?)[\'"].*?>/i', $content, $matches);
            $content_images = [];
            foreach ($matches[1] as $val) {
                try {
                    $ret = $uploadMgr->upload($uploadMgr->download($val), 'image');
                    if ($ret['code'] == 0) {
                        $content_images[$val] = $ret['data']['path'];
                    }
                } catch (\Throwable $th) {
                    continue;
                }
            }
            foreach ($content_images as $key => $val) {
                $content = str_replace($key, $val, $content);
            }
            $item = [
                'language_id'  => $v['country_code'] == 'ZH' ? 1 : 2,
                'category_id'  => $category_map[$v['cid']],
                'title'        => $v['name'],
                'author'       => $v['writer'],
                'source'       => $v['source'],
                'image'        => $image,
                'desc'         => $v['description'],
                'recommend'    => $v['recommend'],
                'sort'         => $v['sort'] == 9999 ? 0 : $v['sort'],
                'link'         => $v['jump_link'],
                'content'      => $content,
                'view_count'   => $v['viewcount'],
                'praise_count' => $v['zancount'],
                'seo_title'    => $v['seo_title'],
                'seo_keywords' => $v['seo_keyword'],
                'seo_desc'     => $v['seo_description'],
                'enabled'      => $v['stat'],
                'release_time' => date('Y-m-d H:i:s', $v['createtime'])
            ];
            Db::name('article')->insert($item);

            $this->println('迁移文章ID：' . $v['id']);
        }
    }

    // 迁移FAQ
    private function migrateFaq()
    {
        $faq = Db::connect('old')
            ->name('fq')
            ->where('id', '>', 0)
            ->where('stat', '>=', 0)
            ->select();

        $uploadMgr = new UploadMannager();
        foreach ($faq as $key => $val) {
            $image = '';
            $ret = $uploadMgr->upload($uploadMgr->download($val['picture']), 'image', 'faq');
            if ($ret['code'] == 0) {
                $image = $ret['data']['path'];
            } else {
                $image = $ret['msg'];
            }
            $content = explode("\n", $val['content']);
            $content = '<p>' . implode("</p><p>", $content) . '</p>';
            $item = [
                'language_id' => $val['country_code'] == 'ZH' ? 1 : 2,
                'image'       => $image,
                'question'    => $val['name'],
                'answer'      => $content,
                'recommend'   => $val['is_home'],
                'sort'        => $val['sort'] == 9999 ? 0 : $val['sort']
            ];
            Db::name('faq')->insert($item);
        }
    }

    // 迁移附件
    private function migrateAttachment($category_map = [])
    {
        $attachment = Db::connect('old')
            ->name('download')
            ->where('id', '>', 0)
            ->where('country_code', 'in', ['ZH', 'US'])
            ->order(['id' => 'asc'])
            ->cursor();

        $uploadMgr = new UploadMannager();
        foreach ($attachment as $key => $val) {
            $image = '';
            $image_ret = $uploadMgr->upload($uploadMgr->download($val['picture']), 'image');
            if ($image_ret['code'] == 0) {
                $image = $image_ret['data']['path'];
            } else {
                $image = $image_ret['msg'];
            }
            $paths  = explode(',', $val['downloadpath']);
            $btns   = explode(',', $val['downloadpath64']);
            $attach = [];
            foreach ($paths as $k => $path) {
                $file_path = '';
                if (\think\helper\Str::contains($path, "oos.oricogroup.com")) {
                    $file_path = $path;
                } else {
                    $attach_ret = $uploadMgr->upload($uploadMgr->download($path), 'attachment');
                    if ($attach_ret['code'] == 0) {
                        $file_path = $attach_ret['data']['path'];
                    } else {
                        $file_path = $attach_ret['msg'];
                    }
                }

                $file_ext =  '';
                $last_sper_idx = strrpos($path, '.');
                if ($last_sper_idx !== false) {
                    $file_ext = substr($path, $last_sper_idx + 1);
                    if (substr($path, $last_sper_idx - 3, $last_sper_idx) == 'tar') {
                        $file_ext = 'tar.' . $file_ext;
                    }
                }

                $attach[] = [
                    'file_path' => $file_path,
                    'file_ext'  => $file_ext,
                    'btn_name'  => $btns[$k],
                ];
            }
            $item = [
                'language_id'      => $val['country_code'] == 'ZH' ? 1 : 2,
                'category_id'      => $category_map[$val['cid']],
                'name'             => $val['name'],
                'desc'             => $val['description'],
                'image'            => $image,
                'applicable_to'    => $val['app_model'],
                'support_platform' => $val['support_os'],
                'attach'           => json_encode($attach),
                'sort'             => $val['sort'] == 9999 ? 0 : $val['sort'],
                'recommend'        => $val['recommend'],
                'seo_title'        => $val['seo_title'],
                'seo_keywords'     => $val['seo_keyword'],
                'seo_desc'         => $val['seo_description'],
                'deleted_at'       => $val['stat'] == -1 ? date('Y-m-d H:i:s') : null
            ];
            Db::name('attachment')->insert($item);
            $this->println('迁移附件ID：' . $val['id']);
        }
    }

    // 迁移视频
    private function migrateVideoCategory()
    {
        $categorys = Db::connect('old')
            ->name('video_category')
            ->where('id', '>', 0)
            ->where('country_code', 'in', ['ZH', 'US'])
            ->order(['id' => 'asc'])
            ->cursor();

        foreach ($categorys as $val) {
            $item = [
                'language_id' => $val['country_code'] == 'ZH' ? 1 : 2,
                'name'        => $val['name'],
                'sort'        => $val['sort'] == 9999 ? 0 : $val['sort'],
                'is_show'     => $val['isshow']
            ];
            Db::name('video_category')->insert($item);
            $this->println('迁移视频分类ID：' . $val['id']);
        }
    }

    // 迁移视频
    private function migrateVideo($category_map = [])
    {
        $videos = Db::connect('old')
            ->name('video')
            ->where('id', '>', 844)
            ->where('country_code', 'in', ['ZH', 'US'])
            ->order(['id' => 'asc'])
            ->cursor();

        $uploadMgr = new UploadMannager();
        foreach ($videos as $val) {
            $image = '';
            $image_ret = $uploadMgr->upload($uploadMgr->download($val['picture']), 'image', 'video');
            if ($image_ret['code'] == 0) {
                $image = $image_ret['data']['path'];
            } else {
                $image = $image_ret['msg'];
            }

            $video = '';
            if (!empty($val['videourl'])) {
                $video = $val['videourl'];
            } else {
                $video_ret = $uploadMgr->upload($uploadMgr->download($val['videopath']), 'video', 'video');
                if ($video_ret['code'] == 0) {
                    $video = $video_ret['data']['path'];
                } else {
                    $video = $video_ret['msg'];
                }
            }
            $item = [
                'language_id'      => $val['country_code'] == 'ZH' ? 1 : 2,
                'category_id'      => $category_map[$val['cid']],
                'name'             => $val['name'],
                'desc'             => $val['description'],
                'image'            => $image,
                'video'            => $video,
                'sort'             => $val['sort'] == 9999 ? 0 : $val['sort'],
                'recommend'        => $val['recommend'],
                'seo_title'        => $val['seo_title'],
                'seo_keywords'     => $val['seo_keyword'],
                'seo_desc'         => $val['seo_description'],
                'deleted_at'       => $val['stat'] == -1 ? date('Y-m-d H:i:s') : null
            ];
            Db::name('video')->insert($item);
            $this->println('迁移视频ID：' . $val['id']);
        }
    }
}

class UploadMannager
{
    const   UPLOAD_BASE_API    = 'http://dev.ow.f2b211.com';
    const   DOWNLOAD_BASE_API  = 'https://www.orico.com.cn';
    const   DOWNLOAD_TEMP_PATH = '/var/www/html/orico-official-website/public/migrate_temp_images';
    private $username          = 'admin';
    private $password          = 'Aa-1221';
    private $token             = '';
    private $retrys            = [];

    public function __construct()
    {
        // 登录获取token
        $this->token = $this->getAuthorization();
    }

    // 下载图片
    public function download($file_name)
    {
        if (empty($file_name)) {
            return '';
        }
        $url = $file_name;
        if (\think\helper\Str::startsWith($file_name, 'http')) {
            $need = 'orico.com.cn';
            if (!\think\helper\Str::contains($file_name, $need)) {
                if (\think\helper\Str::contains($file_name, 'https')) {
                    // 避免https过期情况
                    $url = 'http://' . \think\helper\Str::substr($file_name, mb_strlen('https://'));
                }
            } else {
                $url = self::DOWNLOAD_BASE_API . \think\helper\Str::substr($file_name, mb_strpos($file_name, $need) + mb_strlen($need));
            }
            $file_name = '/' . \think\helper\Str::substr($url, mb_strpos($url, '://') + 3);
        } else {
            $url = self::DOWNLOAD_BASE_API . str_replace(" ", "%20", $file_name);
        }
        
        $file_path = self::DOWNLOAD_TEMP_PATH . $file_name;
        $opts = [
            'http' => [
                'method' => 'GET',
                'header' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
            ]
        ];
        $context = stream_context_create($opts);
        $file    = file_get_contents($url, false, $context);
        $dir     = dirname($file_path);
        if (!is_dir($dir)) {
            mkdir($dir, 0777, true);
        }
        file_put_contents($file_path, $file, FILE_USE_INCLUDE_PATH);
        return $file_path;
    }

    // 上传图片
    public function upload($file_path, $field_name, $module = 'unknown') {
        if (empty($file_path)) {
            return ['code' => 0, 'msg' => 'file_path为空', 'data' => ['path' => '']];
        }
        if (\think\helper\Str::startsWith($file_path, 'http')) {
            return ['code' => 0, 'msg' => 'file_path为http', 'data' => ['path' => $file_path]];
        }

        switch($field_name){
            case 'image': 
                $url_path = "/admapi/v1/images/$module/upload";
                break;
            case 'attachment':
                $url_path = '/admapi/v1/attachment/upload';
                break;
            case 'video':
                $url_path = "/admapi/v1/video/$module/upload";
                break;
            default:
                $url_path = "/admapi/v1/images/$module/upload";
                break;
        }
        $ch        = curl_init(self::UPLOAD_BASE_API . $url_path);
        $post_data = [
            $field_name => new \CURLFile($file_path)
        ];
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $this->token
        ]);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        // 登录失效
        if ($http_code == 401) {
            $this->token = $this->getAuthorization();
            if (!isset($this->retrys[$file_path])) {
                $this->retrys[$file_path] = 0;
            }
            if ($this->retrys[$file_path] > 0) {
                throw new \Exception('[' . $file_path . ']上传重试失败');
            }
            $this->retrys[$file_path] += 1;
            return $this->upload($file_path, $field_name);
        }

        if ($http_code != 200 && $http_code != 401) {
            print($file_path . PHP_EOL);
            print($field_name . PHP_EOL);
            print(self::UPLOAD_BASE_API . $url_path . PHP_EOL);
            print($response . PHP_EOL);
            print($http_code . PHP_EOL);
        }

        $ret = json_decode($response, true);
        if (empty($ret)) {
            throw new \Exception($response);
        }
        return $ret;
    }

    // 登录获取token
    private function getAuthorization()
    {
        $ch = curl_init(self::UPLOAD_BASE_API . '/admapi/v1/user/login');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, [
            'username' => $this->username,
            'password' => md5($this->password)
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($http_code != 200) {
            throw new \Exception('获取token失败');
        }

        $result = json_decode($response, true);
        if ($result['code'] != 0) {
            throw new \Exception($result['msg']);
        } 

        return $result['data']['token'];
    }
}
