<?php

class SignInModel extends CI_Model
{
    private $signInTable;
    private $propsConfig;
    private $ssGroupList;
    private $payFixFields;

    private $basicFieldsMap = [
        '唯一ID' => 'uuid',
        '等级下限' => 'min_level',
        '等级上限' => 'max_level',
        '购买价格' => 'price',
        '数数分组' => 'ss_group',
        '配图' => 'skin_dir',
    ];

    private $extraFieldsMap = [
        '唯一ID' => 'uuid',
        '优惠显示' => 'showMultiple',
    ];

    const MultiNumTypeReward = ['随机金卡', '金币'];

    public function __construct()
    {
        parent::__construct();
        $this->signInTable = otable::signInWeeklyConf();
        $this->propsConfig = oo::commonOprModel('config')->getPropsConfigFromLocal('func');
        $this->ssGroupList = array_column(oo::commonOprModel('config')->getUserGroupConfigByCid(5), 'name', 'id');

        $this->load->library('PHPExcel');
        $this->load->library('PHPExcel/PHPExcel/IOFactory');
        $this->load->library('curl');
    }

    /**
     * 展示配置信息,通过type
     * @param string $type
     * @return array
     */
    public function configList(string $type): array
    {
        $sql = "SELECT * FROM {$this->signInTable} where type='{$type}';";
        $tableData = oo::commonOprDb('games')->getAll($sql);

        foreach ($tableData as &$oneD) {
            $oneD['ss_group'] = json_decode($oneD['ss_group'], true);
            if (empty($oneD['ss_group']) || !is_array($oneD['ss_group'])) {
                $oneD['ssGroup'] = [];
            } else {
                foreach ($oneD['ss_group'] as $oneGroup) {
                    $oneD['ssGroup'][] = $this->ssGroupList[$oneGroup];
                }
            }
            $oneD = array_merge($oneD, json_decode($oneD['other_json'], true));

        }

        return $tableData;
    }

    /**
     * 更新签到配置信息
     */
    public function updateSignInConfig(array $data): int
    {
        $id = $data['id'];
        $type = $data['type'];
        $data['ss_group'] = $this->formatSSGroup($data['ss_group']);
        $data['prize_json'] = $this->formatPrize($data['props'], $type);

        //奖励出错
        if ($data['prize_json'] == -1) {
            return -1;
        }

        unset($data['id'], $data['type'], $data['props']);

        $this->filterFieldNull($this->signInTable, $data);
        $sql = funs::db_updateSQL($this->signInTable, $data, "id=$id and type='{$type}'");

//        error_log($sql);
        oo::commonOprDb('games')->query($sql);

        //删除缓存
        oo::commonOprRedis('games')->delete(okeys::signInConfigKey($type));

        return 0;
    }


    /**
     * 更新配置信息
     */
    public function importConfig($file, string $type): array
    {
        //获取表格数据
        $dataArray = $this->getExcelSheetsArray($file);
        $basicConfArray = $dataArray[0] ?? [];
        $rewardsConfArray = $dataArray[1] ?? [];
        $extraConfArray = $dataArray[2] ?? [];

        //根据imgName取导入数据的字段配置
        self::createImportConfig();

        $this->payFixFields['type'] = $type;

        $basicConf = $this->formatExcelBasicConf($basicConfArray);
        $rewardsConf = $this->formatExcelRewardConf($rewardsConfArray);
        $extraConf = $this->formatExcelExtraConf($extraConfArray);

        $formatGoodsConf = $this->matchGoodsConf($basicConf, $rewardsConf, $extraConf);

        //出现错误
        if ($formatGoodsConf[0] === -1) {
            return $formatGoodsConf;
        }

        //删除同一gtype的记录
        $sql = "SELECT `id` FROM {$this->signInTable} WHERE type='{$type}'";
        $ret = oo::commonOprDb('games')->getAll($sql);
        $idList = implode(',', array_column($ret, 'id'));

        //插入新记录
        $sql = funs::db_intsertArrSQL($this->signInTable, $formatGoodsConf);
        $flag = oo::commonOprDb('games')->query($sql);

        if (!$flag) {
            return [-1, '更新失败'];
        }

        $sql = "DELETE FROM {$this->signInTable} WHERE id IN ({$idList})";
        oo::commonOprDb('games')->query($sql);

        //删除缓存
        oo::commonOprRedis('games')->delete(okeys::signInConfigKey($type));

        return [0, '操作成功'];
    }

    private function formatPrize(array $prizeArr, string $type): string
    {
        if ($type == 'weekly') {
            $dayArr = range(1, 7);
        } else {
            $dayArr = [8, 15, 22, 30];
        }
        $prizeFormat = [];
        foreach ($dayArr as $day) {
            $oneDayPrize = [];
            $oneDayPrize['day'] = $day;
            $onePrize = array_values($prizeArr[$day] ?? []);
            if (empty($onePrize)) {
                return -1;
            }
            foreach ($onePrize as &$item) {
                foreach ($item as &$value)
                    $value = intval($value);
            }
            $oneDayPrize['prize'] = $onePrize;
            $prizeFormat[] = $oneDayPrize;
        }

        return json_encode($prizeFormat);
    }

    private function formatSSGroup(array $ssGroup): string
    {
        return json_encode($ssGroup, JSON_NUMERIC_CHECK);
    }

    /**
     * 获取表的所有字段
     * @param string $tableName
     * @return array
     */
    private function getTableFields(string $tableName): array
    {
        $tableNameArr = explode('.', $tableName);
        $fieldsInfo = oo::commonOprDb('games')->getAll("select COLUMN_NAME from information_schema.COLUMNS
                    where table_name = '{$tableNameArr[1]}' and table_schema = '{$tableNameArr[0]}'");
        return array_column($fieldsInfo, 'COLUMN_NAME');
    }

    private function filterFieldNull(string $tableName, array &$data)
    {
        $fieldArr = $this->getTableFields($tableName);

        foreach ($data as $key => &$value) {
            if ($value === null || !in_array($key, $fieldArr)) {
                unset($data[$key]);
            }
        }
    }

    private function getExcelSheetsArray($file): array
    {
        $xlsReader = IOFactory::createReader('Excel2007');
        $xlsReader->setReadDataOnly(true);
        $xlsReader->setLoadSheetsOnly(true);

        $Sheets = $xlsReader->load($file['url']);
        $sheetsCnt = $Sheets->getSheetCount();

        $dataArray = [];
        for ($i = 0; $i < $sheetsCnt; $i++) {
            $tmpData = $Sheets->getSheet($i)->toArray() ?? [];
            foreach ($tmpData as $k => $item) {
                foreach ($item as $k1 => $value) {
                    $item[$k1] = trim($value);
                }
                $line = array_filter($item);
                if (empty($line)) unset($tmpData[$k]);
            }
            $dataArray[$i] = $tmpData;
        }
        return $dataArray;
    }

    private function formatExcelBasicConf($basicConfArray): array
    {
        $ssGroupReverse = array_flip($this->ssGroupList);

        $fieldArr = [];

        foreach ($basicConfArray[0] as $field) {
            if (is_null($field)) {
                continue;
            }
            $fieldArr[] = $this->basicFieldsMap[$field];
        }

        $recordArr = [];
        for ($i = 1; $i < count($basicConfArray); $i++) {
            $oneRecord = [];
            foreach ($basicConfArray[$i] as $index => $item) {
                if (is_null($item)) {
                    continue;
                }
                if ($fieldArr[$index] == "ss_group") {
                    foreach (explode(',', $item) as $groupName) {
                        $oneRecord['ss_group'][] = intval($ssGroupReverse[$groupName]);
                    }
                } else {
                    $oneRecord[$fieldArr[$index]] = $item;
                }
            }
            $recordArr[] = $oneRecord;
        }

        return $recordArr;
    }

    /**
     *  解析奖励配置
     * @param array $stagesRewardsArray
     * @return array
     */
    private function formatExcelRewardConf(array $stagesRewardsArray): array
    {
        $rewardsConf = [];
        for ($line = 1; $line < count($stagesRewardsArray); $line++) {
            $item = $stagesRewardsArray[$line];

            $uuid = $item[0];
            $day = $item[1];
            $rewardsTemp = ['uuid' => $item[0], 'prize_json' => []];
            $rewardList = [];

            //分割奖励
            array_shift($item);
            array_shift($item);
            for ($index = 0; $index < count($item); $index++) {
                $div = intval($index / 3);
                $mod = $index % 3;

                $value = $item[$index];
                if ($mod == 0) {
                    if (empty($value)) {
                        break;
                    }
                    $rewardList[$div]['rewardName'] = $value;
                } elseif ($mod == 1) {
                    $rewardList[$div]['isFixNum'] = $value;
                } else {
                    $rewardList[$div]['rewardNum'] = $value;
                }
            }

            //格式化奖励
            $oneDayReward = [];
            foreach ($rewardList as $oneReward) {
                list($propsId, $skin) = $this->parseRewardTypeAndSkinId($oneReward['rewardName']);

                #非金币奖励，全部固定值奖励
                if (!in_array($oneReward['rewardName'], self::MultiNumTypeReward)) {
                    $oneReward['isFixNum'] = '是';
                }

                #固定值奖励 1 按等级取值 2
                $rtype = ($oneReward['isFixNum'] == '是') ? 1 : 2;

                #奖励数值
                $num = $oneReward['rewardNum'];

                $oneDayReward[] = [
                    "propsId" => intval($propsId),
                    "num" => intval($num),
                    "rtype" => intval($rtype),
                    "skin" => intval($skin)
                ];
            }

            $rewardsConf[$uuid][] = ['day' => $day, 'prize' => $oneDayReward];
        }

        return $rewardsConf;
    }

    /**
     *  解析额外字段配置
     * @param $extraConfArray
     * @return array
     */
    private function formatExcelExtraConf($extraConfArray): array
    {
        if (empty($extraConfArray)) {
            return [];
        }

        $fieldArr = [];
        foreach ($extraConfArray[0] as $field) {
            $fieldArr[] = $this->extraFieldsMap[$field];
        }

        $extraConf = [];
        for ($i = 1; $i < count($extraConfArray); $i++) {
            $oneExtra = [];
            foreach ($extraConfArray[$i] as $index => $item) {
                $oneExtra[$fieldArr[$index]] = $item;
            }
            $extraConf[] = $oneExtra;
        }

        return $extraConf;
    }

    //合并匹配数组，根据唯一ID
    private function matchGoodsConf(array $basicConf, array $rewardsConf, array $extraConf): array
    {
        //表中的所有字段
        $fieldArr = $this->getTableFields($this->signInTable);

        $allRecordInfo = [];
        foreach ($basicConf as $oneBaseConf) {
            $matchTimes = 0;
            foreach ($rewardsConf as $uuid => $oneRewardConf) {
                //匹配上了
                if ($oneBaseConf['uuid'] == $uuid) {
                    $matchTimes++;

                    //检查奖励的合法性
                    foreach ($oneRewardConf as $oneDayPrize) {
                        foreach ($oneDayPrize['prize'] as $onePrize) {
                            if (empty($onePrize['propsId'])) {
                                $jsonStr = json_encode($onePrize);
                                return [-1, "唯一ID：{$uuid},存在不合法的奖励项，propInfo：{$jsonStr}"];
                            }
                        }
                    }

                    $oneBaseConf['prize_json'] = $oneRewardConf;
                    $oneRecordInfo = $this->formatRecordInfo($fieldArr, $oneBaseConf);

                    //额外字段
                    if (!empty($extraConf)) {
                        foreach ($extraConf as $oneExtra) {
                            if ($oneBaseConf['uuid'] == $oneExtra['uuid']) {
                                unset($oneExtra['uuid']);
                                $oneRecordInfo['other_json'] = $oneExtra;
                            }
                        }
                    }

                    unset($oneRecordInfo['id']);
                    $allRecordInfo[] = $oneRecordInfo;
                }
            }
            //没匹配到奖励
            if (empty($matchTimes)) {
                return [-1, "唯一ID：{$oneBaseConf['uuid']},未匹配到奖励"];
            }
        }

        return $allRecordInfo;
    }

    # 通过奖励名 解析奖励类型以及皮肤id
    private function parseRewardTypeAndSkinId($rewardName): array
    {
        foreach ($this->propsConfig as $oneProps) {
            //对比奖励名
            if ($oneProps['name'] == $rewardName) {
                return [$oneProps['id'], ''];
            }

            if (empty($oneProps['skinList'])) {
                continue;
            }

            //有皮肤列表时，对比皮肤名
            foreach ($oneProps['skinList'] as $oneSkin) {
                if ($oneSkin['name'] == $rewardName) {
                    return [$oneProps['id'], $oneSkin['id']];
                }
            }
        }

        return ['', ''];
    }

    private function formatRecordInfo(array $fieldArr, array $recordInfo): array
    {
        $dataInfo = array_merge($this->payFixFields, $recordInfo);
        $recordFormat = [];
        foreach ($fieldArr as $field) {
            if (isset($dataInfo[$field])) {
                $recordFormat[$field] = $dataInfo[$field];
            }
        }
        return $recordFormat;
    }

    private function createImportConfig()
    {
        $payFixFields = [
            'other_json' => [],
        ];

        $this->payFixFields = $payFixFields;
    }
}