<?php

namespace tw\command;

use app\admin\model\store\StoreProduct;
use app\admin\model\store\StoreProductAttr;
use app\admin\model\store\StoreProductAttrResult;
use app\admin\model\store\StoreProductAttrValue;
use app\api\controller\board\UserBoardController;
use app\models\store\StoreProductRule;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
use \think\facade\Config;
// use MeiliSearch\Client;
use tw\lib\robot\Robots;

/**
 * 维护命令，使用 nginx 帐号运行
 * 
 * sudo -u http php think maintain ***
 */
class Maintain extends Command
{
    const PREV_YEAR = 21;   // 活动的上一年标志。比如`幸运2021` 更新为 `幸运2022`，这个值就是 21

    protected $NEW_YEAR;    // new year value
    protected $LUCKY;

    protected function configure()
    {
        $this->setName('maintain')
            ->addArgument('category', Argument::REQUIRED, 'act|prod|trash|reindex|robot_order|order_reset|none')
            ->setDescription('maintain some application data.');
    }

    protected function init(Input $input, Output $output)
    {
        global $argv;
        $argv[1] = $input->getArgument('category') ?? 'none';
    }

    protected function execute(Input $input, Output $output)
    {
        $category = $input->getArgument('category');
        if (is_callable([$this, $category])) {
            $this->$category();
        } else {
            console::log('bad argument ', $category);
        }
    }

    /**
     * 修改幸运 2021 后面的 2021
     * 
     * IMPORTANT:
     * 修改步骤：
     * 1. 修改 eb_store_category 中分类名称
     * 2. 修改 congit/activity 中配置 
     * 3. 执行 php think maintain act
     */
    protected function act()
    {
        console::log(__FUNCTION__);
        $act_parts = Config::get('activity.lucky_spec_items', []);
        if (is_array($act_parts) && count($act_parts) > 1) {
            $this->NEW_YEAR = intval($act_parts[1]);
        } else {
            console::log('bad config: activity.lucky_spec_items. quit.');
            return;
        }
        $act_name = Config::get('activity.lucky_spec_name', '');
        $this->LUCKY = $act_name;

        $this->_update_StoreProductAttr();
        $this->_update_StoreProductAttrValue();
        $this->_update_StoreProductAttrResult();
        $this->_udpate_StoreProductRule();
    }

    /**
     * 修改
     */
    private function _update_StoreProductAttr()
    {
        $rows = StoreProductAttr::getLuckyAttrs($this->LUCKY);

        $changed = false;
        foreach ($rows as &$row) {
            $changed = false;
            //
            if (
                is_array($row['attr_values']) &&
                count($row['attr_values']) > 1 &&
                intval($row['attr_values'][1]) == self::PREV_YEAR
            ) {
                $row['attr_values'][1] = $this->NEW_YEAR;
                $changed = true;
            }
            // sql
            if ($changed) {
                try {
                    $data['attr_values'] = implode(',', $row['attr_values']);
                    // console::log($row);
                    $ok = StoreProductAttr::where('product_id', $row['product_id'])
                        ->where('attr_name', $row['attr_name'])
                        ->update($data);
                    if (!$ok) {
                        console::log('execute sql failed.');
                    }
                } catch (\Exception $e) {
                    console::log('execute sql exception:', $e->getMessage());
                }
            }
        } // foreach
        console::log('table StoreProductAttr updated.');
    }

    private function _update_StoreProductAttrValue()
    {
        $rows = StoreProductAttrValue::where('suk', 'like', '%' . self::PREV_YEAR . '%')->select()->toArray();
        foreach ($rows as $row) {
            $changed = false;

            $suks = explode(',', $row['suk']);  // array
            // replace 
            for ($i = 0; $i < count($suks); $i++) {
                if (intval($suks[$i]) == self::PREV_YEAR) {
                    $suks[$i] = $this->NEW_YEAR;
                    $changed = true;
                }
            }

            if (!$changed) {
                continue;
            }

            // $data['unique'] = sp_random_string(); //  $row['unique'];
            $data['suk'] = implode(',', $suks); // string again
            try {
                $ok = StoreProductAttrValue::edit($data, $row['unique'], 'unique');
                if (!$ok) {
                    console::log('update StoreProductAttrValue failed.', $ok);
                }
            } catch (\Exception $e) {
                console::log('update StoreProductAttrValue exception:', $e->getMessage());
            }
        }
    }

    private function _update_StoreProductAttrResult()
    {
        $rows = StoreProductAttrResult::where("1=1")->field('product_id,result')->select()->toArray();
        $changed = false;
        foreach ($rows as $row) {
            $changed = false;
            $result = json_decode($row['result'], true);

            if (isset($result['attr']) && count($result['attr']) > 0) {
                foreach ($result['attr'] as &$attr) {
                    if (!isset($attr['value']) || $attr['value'] != $this->LUCKY) {
                        continue;
                    }

                    if (isset($attr['detail']) && count($attr['detail']) > 0) {
                        for ($i = 0; $i < count($attr['detail']); $i++) {
                            // update
                            if (intval($attr['detail'][$i]) == self::PREV_YEAR) {
                                $attr['detail'][$i] = strval($this->NEW_YEAR);
                                $changed = true;
                            }
                        }
                    }
                }
            }

            if (isset($result['value']) && count($result['value']) > 0) {
                foreach ($result['value'] as &$value) {
                    foreach ($value as $k => $v) {
                        if (substr($k, 0, 5) == 'value' && intval($v) == self::PREV_YEAR) {
                            $value[$k] = strval($this->NEW_YEAR);
                            $changed = true;
                        } else if ($k == 'detail') {
                            foreach ($v as $kk => $vv) {
                                // update
                                if ($kk == $this->LUCKY && intval($vv) == self::PREV_YEAR) {
                                    $value[$k][$kk] = strval($this->NEW_YEAR);
                                    $changed = true;
                                }
                            }
                        }
                    }
                }
            }

            if (!$changed) {
                continue;
            }

            try {
                $data['result'] = json_encode($result);
                // console::log($row['product_id']);
                $ok = StoreProductAttrResult::where('product_id', $row['product_id'])
                    ->update($data);
                if (!$ok) {
                    console::log('sql execute error:', StoreProductAttrResult::getErrorInfo());
                }
            } catch (\Exception $e) {
                console::log('exception:', $e->getMessage());
            }
        }
    }

    private function _udpate_StoreProductRule()
    {
        $rows = StoreProductRule::where('rule_name', 'like', '%' . $this->LUCKY . '%')->select()->toArray();
        $changed = false;
        foreach ($rows as $row) {
            $changed = false;
            $values = json_decode($row['rule_value'], true);
            if (is_array($values)) {
                foreach ($values as &$value) {
                    if (!isset($value['value']) && $value['value'] != $this->LUCKY) {
                        continue;
                    }

                    if (
                        isset($value['detail']) &&
                        is_array($value['detail']) &&
                        count($value['detail']) > 0
                    ) {

                        for ($i = 0; $i < count($value['detail']); $i++) {
                            if (intval($value['detail'][$i]) == self::PREV_YEAR) {
                                $value['detail'][$i] = strval($this->NEW_YEAR);
                                $changed = true;
                            }
                        }
                    }
                }
            }

            if (!$changed) {
                continue;
            }

            $data['rule_value'] = json_encode($values);

            try {
                console::log($values);
                $ok = StoreProductRule::where('id', $row['id'])->update($data);
                if (!$ok) {
                    console::log('StoreProductRule failed:', StoreProductRule::getErrorInfo());
                }
            } catch (\Exception $e) {
                console::log('StoreProductRule exception:', $e->getMessage());
            }
        }
    }

    /**
     * 让一定时间之前的商品下架
     */
    public function prod()
    {
        $now = time();
        $days = sys_config_int('off_days_before');
        if ($days <= 0) {
            warnlog("maintain prod quit. off_days_before=$days");
            return;
        }

        $days_ago = $now - $days * SECONDS_OF_ONEDAY;
        // 找到过期商品
        $products = StoreProduct::where([
            'is_del' => 0,
            'is_show' => 1,
        ])->where('add_time', '<', $days_ago)
            ->select()->toArray();
        // 下架
        $ids = [];
        foreach ($products as $p) {
            $ids[] = $p['id'];
        }
        $ret = Storeproduct::where('id', 'in', $ids)->update([
            'is_show' => 0,
        ]);
        // 记录日志
        warnlog("auto off-shelves executed: days=$days, ts=$days_ago, ret=$ret, ids=" . json_encode($ids));
    }

    /**
     * 删除过期日志等数据
     */
    protected function trash()
    {
    }

    /**
     * 备份数据库
     * 
     * 后台备份功能超出内存限制
     */
    protected function backup()
    {
        $db = config('database.connections.' . config('database.default'));
        $user = $db['username'];
        $password = $db['password'];
        $host = $db['hostname'];
        $database = $db['database'];
        $now = time();
        $dir = "backup-$now.sql";
        exec("mysqldump --user={$user} --password={$password} --host={$host} {$database} --result-file={$dir} 2>&1", $output);
        warnlog('backup database:' . json_encode($output));
    }

    /**
     * 创建机器人
     */
    protected function robot_init()
    {
        Robots::to_redis();
        infolog('robots created.');
    }

    /**
     * 机器人定时下单
     */
    protected function robot_order()
    {
        Robots::order();
        (new UserBoardController)->cache_board();
        infolog('robot make orders');
    }

    /**
     * 每日重置机器人日榜数据
     */
    protected function robot_reset()
    {
        Robots::to_redis();
        infolog('robot reset');
    }

    /**
     * 重新索引商品
     * 
     * 1.用于常见的商品分类展示。
     * 2.用于关键字搜索
     * 3.用于按价格/销量/好评度/上线时间排序
     * 
     * TODO:
     * - 新增/上架商品: 新增索引
     * - 删除/下架商品：删除索引
     * - 編輯商品：重新索引
     * 
     * cur: 當前新增的商品不會立即出現在搜索結果，而是每次都重新索引全部。
     * 因为这样实现简单，另外商品总的数据量不会很大
     * 
     */
    // protected function reindex()
    // {
    //     $ml = new Client(config('meilisearch.addr'), config('meilisearch.key'));
    //     // remove first
    //     $ml->deleteIndex('products');
    //     // rebuild
    //     $idx = $ml->index('products');

    //     // setup
    //     $idx->updateSearchableAttributes([
    //         'store_name',
    //         'store_info',
    //         'keyword',
    //     ]);
    //     $idx->updateFilterableAttributes([
    //         'cate_id',
    //         'price',
    //         'is_hot',
    //         'is_benefit',
    //         'is_best',
    //         'is_new',
    //         'add_time',
    //         'is_good',
    //     ]);
    //     $idx->updateSortableAttributes([
    //         'price',
    //         'sales',
    //         'cost',
    //         'browse',
    //     ]);
    //     // get all products
    //     $page = 1;
    //     $limit = 20;
    //     $count = 0;
    //     while (true) {
    //         $products = StoreProduct::where('is_del', 0)
    //             ->where('stock', '>', 0)
    //             ->where('is_show', 1)
    //             ->field('id,image,slider_image,store_name,
    //                 store_info,keyword,cate_id,price,vip_price,ot_price,postage,
    //                 unit_name,sort,sales,stock,
    //                 is_hot,is_benefit,is_best,is_new,add_time,is_postage, 
    //                 give_integral,cost,is_good,ficti,spec_type,browse')
    //             ->page($page, $limit)->select()->toArray();

    //         $count += count($products);

    //         if (count($products) > 0) {
    //             $idx->addDocuments($products, 'id');
    //         }
    //         // no more rows
    //         if (count($products) < $limit) {
    //             break;
    //         }

    //         // loop
    //         $page += 1;
    //     }
    //     echo "re-index all products finished. total=$count" . PHP_EOL;
    // }

    protected function _all_products_on_sale()
    {
    }

    protected function none()
    {
        // Robots::build();
        // print_r(Robots::$robots);
        // Robots::to_redis();
        // Robots::order();
        $robots = Robots::first_n_by_value(10);
        console::log($robots);
    }
}
