<?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)
    {
        ini_set('pcre.backtrack_limit', 10000000);
        ini_set('default_socket_timeout', 1);
        
        // 指令输出
        $this->println = function ($msg) use ($output) {
            $output->writeln($msg);
        };

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

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

            // 迁移产品属性
            // $this->migrateProductAttr();

            // 迁移产品
            // $this->migrateProduct();

            // $files = $this->getLocalImages('/var/www/html/orico-official-website/public/migrate_temp_images');
            // print_r($files);
            // foreach ($files as $file) {
            //     unlink($file);
            // }

            // 迁移产品关联产品数据
            // $this->migrateProductRelated();

            // 迁移产品购买链接
            // $this->migrateProductPurchaseLinks();

            // 迁移文章
            // $this->migrateArticle([
            //     16 => 7,
            //     31 => 9,
            //     32 => 8,
            //     68 => 10,
            //     69 => 11,
            //     70 => 12,
            //     71 => 13,
            //     72 => 14,
            //     73 => 15,
            //     74 => 16,
            //     75 => 17,
            //     78 => 19,
            //     79 => 20,
            //     80 => 21,
            //     81 => 22,
            //     82 => 23,
            //     83 => 24,
            //     84 => 25,
            //     85 => 26
            // ]);

            // 迁移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
            // ]);

            // $this->test();

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

    // 获取本地图片
    private function getLocalImages($path)
    {
        $files = [];
        $dir = dir($path);
        while (false !== ($entry = $dir->read())) {
            if ($entry == '.' || $entry == '..') {
                continue;
            }
            $file = $path . '/' . $entry;
            if (is_dir($file)) {
                $files = array_merge($files, $this->getLocalImages($file));
            } else {
                $info = stat($file);
                if ($info['size'] == 0) {
                    $files[] = $file;
                }
                // if (!is_valid_image($file)) {
                //     $files[] = $file;
                // }
            }
        }
        return $files;
    }

    // 迁移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();

        $tree = array_to_tree($category, 0, 'pid', 1);
        // 处理数据
        $this->handlerProductCategory($tree, $tco_category_map);
    }
    private function handlerProductCategory($category, $map, $path = []) {
        foreach ($category as $val) {
            $key = sprintf("%s_%s", $val['id'], $val['country_code']);
            if ($val['pid'] == 0) {
                $path = [];
            } else {
                if (!in_array($val['pid'], $path)) $path[] = $val['pid'];
            }
            $item = [
                'id'                   => $val['id'],
                'language_id'          => $val['country_code'] == 'ZH' ? 1 : 2,
                'unique_id'            => uniqid('PRO_CATE_'),
                'pid'                  => $val['pid'],
                'name'                 => $val['name'],
                'path'                 => implode(',', $path),
                '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, $path);
            }
        }
    }

    // 迁移产品属性
    private function migrateProductAttr()
    {
        $products = Db::connect('old')
            ->name('product')
            ->where('country_code', 'in', ['ZH', 'US'])
            ->where('product_attr', '<>', '')
            ->order(['id' => 'asc'])
            ->cursor();

        $exists = [];
        foreach ($products as $v)
        {
            $attrs = [];
            $product_attr = json_decode($v['product_attr'], true);
            foreach ($product_attr as $attr) {
                if (!in_array($attr, ['颜色', 'Color']) && !in_array($attr, $exists)) {
                    $attrs[] = [
                        'language_id' => $v['country_code'] == 'ZH' ? 1 : 2,
                        'attr_type'   => 2,
                        'attr_name'   => $attr,
                        'is_system'   => 0
                    ];
                    $exists[] = $attr;
                }
            }
            if (empty($attrs)) {
                continue;
            }
            Db::name('product_attr')->insertAll($attrs);
        }
    }

    // 迁移产品
    private function migrateProduct()
    {
        $attrs_map = [];
        $attrs_dict = Db::name('product_attr')
            ->withoutField(['created_at', 'updated_at', 'deleted_at'])
            ->select();
        foreach ($attrs_dict as $attr)
        {
            $code = $attr['language_id'] == 1 ? 'ZH' : 'US';
            if (!isset($attrs_map[$code])) {
                $attrs_map[$code] = [];
            }
            $attrs_map[$code][$attr['attr_name']] = $attr['id'];
        }

        $old_db = Db::connect('old');

        $success_map = [];
        $success_arr = []; // include_once(runtime_path() . 'product_success.php');
        foreach ($success_arr as $so) {
            $success_map['p_' . $so['cod_product_id']] = $so;
        }

        $arr = include_once(runtime_path() . 'product_ids.php');
        $products = $old_db->name('product')
            ->where('country_code', 'in', ['ZH', 'US'])
            ->where('id', 'in', array_unique(array_column($arr, 'cod_product_id')))
            ->order(['id' => 'asc'])
            ->cursor();

        $total = 0;
        $uploadMgr = new UploadMannager();
        foreach ($products as $v)
        {
            $start = microtime(true);
            if (isset($success_map['p_' . $v['id']])) {
                continue;
            }

            Db::startTrans();
            try {
                // 处理封面图片
                $image = '';
                $image_ret = $uploadMgr->upload($uploadMgr->download($v['list_bk_img']), 'image', 'product');
                if ($image_ret['code'] == 0) {
                    $image = $image_ret['data']['path'];
                } else {
                    if ($image_ret['code'] != 400) {
                        $image = $image_ret['msg'];
                    }
                }

                // 处理视频
                $video = '';
                $video_ret = $uploadMgr->upload($uploadMgr->download($v['videopath']), 'video', 'video');
                if ($video_ret['code'] == 0) {
                    $video = $video_ret['data']['path'];
                } else {
                    if ($video_ret['code'] != 400) {
                        $video = $video_ret['msg'];
                    }
                }

                // 处理详情中图片
                $content = preg_replace_callback('/<img[^>]*src=[\'"]([^\'"]*?)[\'"][^>]*>/is', function($matches) use ($uploadMgr) {
                    $file_path = '';
                    try {
                        $ret = $uploadMgr->upload($uploadMgr->download($matches[1]), 'image');
                        if ($ret['code'] == 0) {
                            $file_path = $ret['data']['path'];
                        }
                    } catch (\Throwable $th) {
                        $this->println($th->getMessage() . ':' . $th->getLine());
                        return '<img src="解析替换图片失败，请手动处理" alt="" />';
                    }
                    return '<img src="' . $file_path . '" alt="" />';
                }, $v['ld_md_content']??'');

                $item = [
                    'language_id'  => $v['country_code'] == 'ZH' ? 1 : 2,
                    'category_id'  => $v['cid'],
                    'spu'          => $v['brand_id'],
                    'name'         => $v['name'],
                    'short_name'   => $v['shortname'],
                    'cover_image'  => $image,
                    'desc'         => $v['description'],
                    'video_img'    => '',
                    'video_url'    => $video,
                    'is_sale'      => $v['is_onsale'],
                    'is_new'       => $v['isnew'],
                    'is_hot'       => $v['ishot'],
                    'is_show'      => $v['is_show'] == 0 ? 1 : 0,
                    'sort'         => $v['sort'] == 9999 ? 0 : $v['sort'],
                    'detail'       => $content,
                    'status'       => $v['stat'] == -1 ? -1 : 1,
                    'seo_title'    => $v['seo_title'],
                    'seo_keywords' => $v['seo_keyword'],
                    'seo_desc'     => $v['seo_description'],
                    'created_at'   => date('Y-m-d H:i:s', $v['createtime']),
                    'updated_at'   => $v['updatetime'] == 0 ? date('Y-m-d H:i:s', $v['createtime']) : date('Y-m-d H:i:s', $v['updatetime']),
                    'deleted_at'   => $v['stat'] == -1 ? date('Y-m-d H:i:s') : null,
                ];
                // 保存产品数据
                $prod = Db::name('product')
                    ->where('language_id', '=', $item['language_id'])
                    ->where('spu', '=', $item['spu'])
                    ->find();
                if (!empty($prod)) {
                    $id = $prod['id'];
                    Db::name('product')->where('id', '=', $prod['id'])->update($item);
                } else {
                    $id = Db::name('product')->insertGetId($item);
                }

                // 保存产品参数数据
                if (!empty($v['product_view'])) {
                    $prarms = [];
                    $views  = unserialize($v['product_view']);
                    foreach ($views as $p) {
                        $prarms[] = [
                            'product_id' => $id,
                            'name'       => $p['desc_title'],
                            'value'      => $p['desc_desc']
                        ];
                    }
                    $old_params = Db::name('product_params')
                        ->where('product_id', '=', $id)
                        ->select();
                    if ($old_params->isEmpty()) {
                        Db::name('product_params')->insertAll($prarms);
                    } else {
                        foreach ($old_params as $op) {
                            Db::name('product_params')
                                ->where('product_id', '=', $id)
                                ->where('name', '=', $op['name'])
                                ->update([
                                    'value' => $op['value']
                                ]);
                        }
                    }
                }

                // 保存sku数据
                $skus = [];
                $sku_images = $old_db->name('product_image')
                    ->where('product_id', '=', $v['id'])
                    ->where('country_code', '=', $v['country_code'])
                    ->where('stat', '=', 0)
                    ->order(['id' => 'asc'])
                    ->select();
                $images      = [];
                $photo_album = [];
                foreach ($sku_images as $im) {
                    $pkey = $im['sku'];
                    if (empty($pkey)) {
                        if (!empty($im['image_color'])) {
                            $pkey = md5($im['image_color']);
                        } else {
                            $pkey = 0;
                        }
                    }
                    if (!isset($images[$pkey])) {
                        $images[$pkey]        = [];
                        $images[$pkey]['sku'] = $im['sku'];
                        $images[$pkey]['attrs'] = [];
                    }

                    // 处理图册
                    if (!empty($im['image_url'])) {
                        $photos = json_decode($im['image_url'], true);
                        if (empty($photos)) {
                            $photos[] = $im['image_url'];
                        }
                        foreach ($photos as $photo) {
                            $photos_ret = $uploadMgr->upload($uploadMgr->download($photo), 'image', 'product');
                            if ($photos_ret['code'] == 0) {
                                if (!isset($photo_album[$pkey])) {
                                    $photo_album[$pkey] = [];
                                }
                                $photo_album[$pkey][] = $photos_ret['data']['path'];
                            } else {
                                if ($photos_ret['code'] != 400) {
                                    $photo_album[$pkey][] = json_encode($photos_ret);
                                }
                            }
                        }
                    }

                    // 处理属性
                    $attrs = json_decode($im['image_color'], true);
                    if (!empty($attrs)) {
                        foreach ($attrs as $attr) {
                            foreach ($attr as $k => $at) {
                                if ($k != 'sort') {
                                    $attr_value = $at;
                                    if (in_array($k, ['颜色', 'Color'])) {
                                        // if ($k == 'Color') $k = '颜色';
                                        $attr_value = '/static/common/images/colors/' . $at . '.png';
                                    }
                                    $images[$pkey]['color'] = $at;
                                    $images[$pkey]['attrs'][] = [
                                        'attr_id'    => $attrs_map[$v['country_code']][$k],
                                        'attr_value' => $attr_value,
                                    ];
                                }
                            }
                        }
                    } else {
                        $attr_value = $im['image_color'];
                        if (empty($attr_value) || $attr_value == '[]') {
                            $images[$pkey]['attrs'] = [];
                            continue;
                        }
                        if (\think\helper\Str::endsWith($attr_value, ['.png', '.jpg', 'jpeg', '.gif'])) {
                            $photos_ret = $uploadMgr->upload($uploadMgr->download($attr_value), 'image', 'product');
                            if ($photos_ret['code'] == 0) {
                                $attr_value = $photos_ret['data']['path'];
                            }
                        }
                        $attr_arr = [
                            'attr_id'    => $attrs_map[$v['country_code']][$v['country_code'] == 'ZH' ? '颜色' : 'Color'],
                            'attr_value' => $attr_value,
                        ];
                        if (
                            empty($images[$pkey]['attrs']) || !array_some($images[$pkey]['attrs'], function($k, $v) use($attr_arr) {
                                return $v == $attr_arr;
                            })
                        ) {
                            $images[$pkey]['attrs'][] = $attr_arr;
                        }
                    }
                }
                if (!empty($photo_album)) {
                    foreach ($images as $key => $image) {
                        if (isset($photo_album[$key])) {
                            $images[$key]['photo_album'] = $photo_album[$key];
                        }
                    }
                }
                
                $two_images = $old_db->name('product_two_img')
                    ->where('product_id', '=', $v['id'])
                    // ->where('country_code', '=', $v['country_code'])
                    ->where('stat', '=', 0)
                    ->order(['id' => 'asc'])
                    ->select();
                foreach ($two_images as $ti) {
                    $tpkey = 0;
                    if (!empty($ti['image_color'])) {
                        $tpkey = md5($ti['image_color']);
                    }
                    if (!empty($ti['image_url']) && $ti['image_url'] != '[]') {
                        $main_image_ret = $uploadMgr->upload($uploadMgr->download($ti['image_url']), 'image', 'product');
                        if ($main_image_ret['code'] == 0) {
                            $ti['image_url'] = $main_image_ret['data']['path'];
                        }
                    }
                    if (\think\helper\Str::endsWith($ti['image_color'], ['.png', '.jpg', 'jpeg', '.gif'])) {
                        $arrt_ret = $uploadMgr->upload($uploadMgr->download($ti['image_color']), 'image', 'product');
                        if ($arrt_ret['code'] == 0) {
                            $ti['image_color'] = $arrt_ret['data']['path'];
                        }
                    }
                    $skus[] = [
                        'main_image' => $ti['image_url'],
                        'attrs' => [[
                            'attr_id'    => $attrs_map[$v['country_code']][$v['country_code'] == 'ZH' ? '颜色' : 'Color'],
                            'attr_value' => $ti['image_color'],
                        ]],
                        'pkey'      => $tpkey
                    ];
                }

                if (!empty($skus)) {
                    $temp        = [];
                    $temp_images = [];
                    foreach ($skus as $idx => $sku) {
                        $skus[$idx]['product_id']  = $id;
                        foreach ($images as $im) {
                            if (isset($im['color'])) {
                                if (md5($im['color']) == $sku['pkey']) {
                                    $skus[$idx]['sku'] = $im['sku']??null;
                                    $skus[$idx]['photo_album'] = $im['photo_album']??null;
                                    if (!empty($im['attrs'])) {
                                        $skus[$idx]['attrs'] = $im['attrs'];
                                    }
                                    continue 2;
                                }
                            }
                        }

                        if (isset($images[$sku['pkey']])) {
                            $skus[$idx]['sku']         = $images[$sku['pkey']]['sku']??null;
                            $skus[$idx]['photo_album'] = $images[$sku['pkey']]['photo_album']??null;
                            if (!empty($images[$sku['pkey']]['attrs'])) {
                                $skus[$idx]['attrs'] = $images[$sku['pkey']]['attrs'];
                            }
                        } else {
                            if (empty($temp_images)) {
                                $temp_images = array_values($images);
                            }
                            if (!empty($temp_images[$idx])) {
                                $temp = $temp_images[$idx];
                            }
                            if (!empty($temp)) {
                                $skus[$idx]['sku']         = $temp['sku']??null;
                                $skus[$idx]['photo_album'] = $temp['photo_album']??null;
                                if (!empty($temp['attrs'])) {
                                    $skus[$idx]['attrs'] = $temp['attrs'];
                                }
                            }
                        }
                        unset($skus[$idx]['pkey']);
                    }
                } else {
                    foreach ($images as $image) {
                        $skus[] = [
                            'product_id'  => $id,
                            'sku'         => $image['sku']??null,
                            'main_image'  => '',
                            'photo_album' => $image['photo_album']??null,
                            'skus'        => $image['attrs']
                        ];
                    }
                }

                foreach ($skus as $sku) {
                    Db::name('product_sku')->where('product_id', '=', $sku['product_id'])->delete();
                    $sku_id = Db::name('product_sku')->insertGetId([
                        'product_id'  => $sku['product_id'],
                        'sku'         => $sku['sku']??'',
                        'main_image'  => $sku['main_image']??'',
                        'photo_album' => !empty($sku['photo_album']) ? json_encode($sku['photo_album']) : null,
                    ]);
                    if (!empty($sku['attrs'])) {
                        foreach ($sku['attrs'] as $attr) {
                            if (!empty($sku_id)) {
                                Db::name('product_sku_attr')->where('sku_id', '=', $sku_id)->delete();
                                Db::name('product_sku_attr')->insert([
                                    'sku_id'     => $sku_id,
                                    'attr_id'    => $attr['attr_id'],
                                    'attr_value' => $attr['attr_value']
                                ]);
                            }
                        }
                    }
                }

                Db::commit();
                $total += 1;
                file_put_contents(
                    runtime_path() . 'product_success.txt',
                    sprintf('["ow_product_id" => %d, "cod_product_id" => %d]'.PHP_EOL, $id, $v['id']),
                    FILE_APPEND
                );
                $this->println(sprintf('迁移产品ID：%s => %s 【耗时：%s】', $v['id'], $id, round(microtime(true) - $start, 2) . 's'));
            } catch (\Throwable $th) {
                Db::rollback();
                file_put_contents(
                    runtime_path() . 'product_throwable.txt', 
                    sprintf('【%s】 产品【%s】，迁移失败，错误【%s:%d】' . PHP_EOL, date('Y-h-d H:i:s'), $v['id'], $th->getMessage(), $th->getLine()),
                    FILE_APPEND
                );
                file_put_contents(
                    runtime_path() . 'product_throwable_details.txt',
                    (string)$th
                );
            }
        }

        $this->println(sprintf('迁移产品完成，共迁移 %s 条数据', $total));
    }

    // 迁移产品关联产品数据
    private function migrateProductRelated()
    {
        $sources = include_once(runtime_path() . 'product_success.php');
        $maps = [];
        foreach ($sources as $so) {
            $maps[$so['cod_product_id']] = $so;
        }

        $old_db = Db::connect('old');
        $related = $old_db->name('product_related')
            ->where('country_code', 'in', ['ZH', 'US'])
            ->where('stat', '=', 0)
            ->cursor();

        $data = [];
        foreach ($related as $rl) {
            if (empty($maps[$rl['product_id']]) || empty($maps[$rl['related_product_id']])) {
                continue;
            }
            $data[] = [
                'product_id'         => $maps[$rl['product_id']]['ow_product_id'],
                'related_product_id' => $maps[$rl['related_product_id']]['ow_product_id'],
                'desc'               => empty($rl['related_desc']) ? null : $rl['related_desc'],
                'sort'               => $rl['related_sort'] == 9999 ? 0 : $rl['related_sort']
            ];
        }
        Db::name('product_related')->insertAll($data);
    }

    // 迁移产品购买链接
    private function migrateProductPurchaseLinks()
    {
        $platform_maps = [
            1 => 2,
            2 => 1,
            3 => 3,
            4 => 4
        ];

        $sources = include_once(runtime_path() . 'product_success.php');
        $maps = [];
        foreach ($sources as $so) {
            $maps[$so['cod_product_id']] = $so;
        }

        $old_db = Db::connect('old');
        $links = $old_db->name('product_purchase_links')
            ->where('country_code', 'in', ['ZH', 'US'])
            ->where('link', '<>', '')
            ->cursor();

        foreach ($links as $link) {
            if (empty($maps[$link['product_id']])) {
                continue;
            }
            $item = [
                'language_id' => $link['country_code'] == 'ZH' ? 1 : 2,
                'product_id'  => $maps[$link['product_id']]['ow_product_id'],
                'platform_id' => $platform_maps[$link['platform_id']],
                'link'        => $link['link']
            ];
            Db::name('product_purchase_link')->insert($item);
        }
    }

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

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

        // 1634
        $article = Db::connect('old')
            ->name('article')
            ->where('country_code', 'in', ['ZH', 'US'])
            ->where('cid', 'in', array_keys($category_map))
            ->where('id', '=', 351)
            ->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 = preg_replace_callback('/<img[^>]*src=[\'"]([^\'"]*?)[\'"][^>]*>/is', function ($matches) use ($uploadMgr) {
                $file_path = '';
                try {
                    $ret = $uploadMgr->upload($uploadMgr->download($matches[1]), 'image');
                    if ($ret['code'] == 0) {
                        $file_path = $ret['data']['path'];
                    }
                } catch (\Throwable $th) {
                    $this->println($th->getMessage() . ':' . $th->getLine());
                    return '<img src="解析替换图片失败，请手动处理" alt="" />';
                }
                return '<img src="' . $file_path . '" alt="" />';
            }, $v['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'],
                'release_time' => date('Y-m-d H:i:s', $v['createtime']),
                'deleted_at'   => $v['stat'] == -1 ? date('Y-m-d H:i:s') : null
            ];
            $ret = Db::name('article')
                ->where('language_id', '=', $item['language_id'])
                ->where('title', '=', $item['title'])
                ->find();
            if (empty($ret)) {
                $id = Db::name('article')->insertGetId($item);
            } else {
                $id = $ret['id'];
                Db::name('article')->where('id', '=', $ret['id'])->update($item);
            }

            $this->println(sprintf('迁移文章ID：%s => %s', $v['id'], $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']
            ];
            $ret = Db::name('faq')
                ->where('language_id', '=', $item['language_id'])
                ->where('question', '=', $item['question'])
                ->find();
            if (empty($ret)) {
                Db::name('faq')->insert($item);
            } else {
                Db::name('faq')->where('id', '=', $ret['id'])->update($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('cid', 'in', array_keys($category_map))
            ->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
            ];
            $ret = Db::name('video')
                ->where('language_id', '=', $item['language_id'])
                ->where('name', '=', $item['name'])
                ->find();
            if (empty($ret)) {
                Db::name('video')->insert($item);
            } else {
                Db::name('video')->where('id', '=', $ret['id'])->update($item);
            }
            
            $this->println('迁移视频ID：' . $val['id']);
        }
    }
}

class UploadMannager
{
    const   UPLOAD_BASE_API    = 'http://ow.f2b211.com';
    const   DOWNLOAD_BASE_API  = 'http://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            = [];
    private $maps              = [];

    public function __construct()
    {
        // 登录获取token
        $this->token = $this->getAuthorization();
        $this->maps = []; // include_once(runtime_path() . 'fiber_product_image_mapping.php');
    }

    // 下载图片
    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);
        }
        elseif (\think\helper\Str::startsWith($file_name, 'data:image/')) {
            $idx = strpos($file_name, ';');
            $file_type = substr($file_name, 0, $idx);
            $base64_image = preg_replace('#^data:image/\w+;base64,#i', '', $file_name);
            $file_data = base64_decode($base64_image, true);
            $file_path = self::DOWNLOAD_TEMP_PATH . '/uploads/' . date('Ymd') . '/' . uniqid() . '.' . substr($file_type, 11);
            $dir       = dirname($file_path);
            if (!is_dir($dir)) {
                mkdir($dir, 0777, true);
            }
            if (!file_put_contents($file_path, $file_data, FILE_USE_INCLUDE_PATH)) {
                throw new \Exception('转换base64图片失败');
            }
            return $file_path;
        } 
        else {
            $url = self::DOWNLOAD_BASE_API . str_replace(" ", "%20", $file_name);
        }
        
        $file_path = self::DOWNLOAD_TEMP_PATH . $file_name;
        if (file_exists($file_path)) {
            if (!is_valid_image($file_path)) {
                return '';
            }
            return $file_path;
        }

        // 使用curl下载
        $file = file_get_withcurl($url);
        $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' => 400, 'msg' => 'file_path为空', 'data' => ['path' => '']];
        }
        if (\think\helper\Str::startsWith($file_path, 'http')) {
            return ['code' => 0, 'msg' => 'file_path为http', 'data' => ['path' => $file_path]];
        }
        if (isset($this->maps[$file_path])) {
            return ['code' => 0, 'msg' => '成功', 'data' => ['path' => $this->maps[$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);
        }

        file_put_contents(runtime_path() . 'upload.txt', $response . PHP_EOL, FILE_APPEND);
        $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'];
    }
}

// 使用curl下载图片
function file_get_withcurl($url)
{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 连接超时 10 秒
    curl_setopt($ch, CURLOPT_TIMEOUT, 60);        // 传输超时 30 秒
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 跟随重定向
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0'); // 模拟浏览器 UA

    $data = curl_exec($ch);
    if ($data === false) {
        file_put_contents(runtime_path() . 'file_get_withcurl.txt', sprintf('cURL Error: %s; URL: %s' . PHP_EOL, curl_error($ch), $url), FILE_APPEND);
        return false;
    }
    curl_close($ch);

    return $data;
}

// 检查图片是否有效
function is_valid_image($filepath) 
{
    try {
        $image_size = getimagesize($filepath);
        if ($image_size === false) {
            return false;
        }
        [$width, $height] = $image_size;
        return $width > 0 && $height > 0;
    } catch (\Throwable $e) {
        return false;
    }
}

// 根据自定义函数检查数组
function array_some($array, callable $ca): bool
{
    foreach ($array as $k => $v) {
        if ($ca($k, $v)) return true;
    }

    return false;
}
