<?php

namespace common\libs\export;

use common\helpers\Cache as CacheHelper;
use common\helpers\Date;
use common\helpers\Form;
use common\helpers\http\RemoteUploadApi;
use common\helpers\Tool;
use common\libs\dataList\DataList;
use common\models\Export;
use common\models\Region;
use Yii;
use yii\base\Exception;
use yii\base\StaticInstanceTrait;
use yii\base\Component;

class BaseExport extends Component {
    use StaticInstanceTrait;
    public $moduleId;
    /**
     * @var string the model class name. This property must be set.
     */
    public $modelClass;
    /**
     * @var
     */
    public $listModelClass;
    /**
     * @var int
     */
    public $pageSize = 100;
    /**
     * @var int
     */
    public $totalCount = 0;
    /**
     * @var int
     */
    public $offset = 0;
    /**
     * @var int
     */
    public $limit = 100;
    /**
     * csv文件名称
     * @var null
     */
    public $fileName = null;
    /**
     * 保存的基本路径
     * @var null
     */
    public $saveBasePath = null;
    /**
     * 保存路径
     * @var null
     */
    public $savePath = null;
    /**
     * 正在导出的id
     * 来源于exporting_file表
     * @var null
     */
    public $exportId = null;
    /**
     * 操作人的id
     * @var null
     */
    public $userId = null;
    /**
     * 任务id
     * @var null
     */
    public $taskId = null;
    /**
     * 是否完成
     * @var bool
     */
    public $completed = false;
    /**
     * @var array
     */
    public $params = [];
    /**
     * 文件句柄
     * @var null
     */
    private $_fp = null;
    /**
     * 是否上传远程
     * @var bool
     */
    public $isRemote = true;

    /**
     * @var DataList
     */
    private $_listModel;

    public static function factory($taskId) {
        return new self([
                'taskId' => $taskId
            ]
        );
    }

    /**
     *
     */
    public function init() {
        parent::init();
        $this->isRemote = \Yii::$app->params['isRemoteUpload'];
    }

    /**
     * 生成复杂的路径
     * @return string
     */
    public function pathCreator(...$params) {
        $hash = md5(implode('_', $params));
        $first = substr($hash, 0, 3);
        $second = substr($hash, 3, 2);
        $dir = $first . __DS__ . $second . __DS__;
        return $dir;
    }

    /**
     * 获取文件名
     * @return string|null
     * @throws \Exception
     */
    public function getFileName($ext = '.csv') {
        $this->fileName = date('YmdHis', Date::nowTime()) . random_int(1000, 9999) . $ext;
        return $this->fileName;
    }

    /**
     * 获取保存的路径
     * @return array|null
     */
    public function getSavePath() {
        $this->savePath = $this->pathCreator(date('Ymd', Date::nowTime()));
        return trim($this->savePath, __DS__);
    }

    /**
     * @return bool|string|null
     */
    public function getSaveBasePath() {
        $this->saveBasePath = Yii::getAlias('@backendApi') . __DS__ . 'web' . __DS__ . 'upload' . __DS__ . \Yii::$app->params['excelLocalDir'];
        return trim($this->saveBasePath, __DS__);
    }

    /**
     * 创建目录(递归创建)
     *
     * @param $dir
     * @return string|false
     */
    public function mkdir($dir) {
        if (!is_dir($dir)) {
            $this->mkdir(dirname($dir));
            if (!mkdir($dir, 0777))
                return false;
            @exec('chown -R www:www /'.$dir);
            @exec('chmod -R 777 /'.$dir);
        }
        return $dir;
    }

    /**
     * @return array|mixed
     */
    public function getParams() {
        $this->params = CacheHelper::getAsyncParams($this->taskId);
        return $this->params;
    }

    /**
     * @return mixed|null
     */
    public function getUserId() {
        $this->userId = isset($this->params['userId']) ? $this->params['userId'] : null;
        return $this->userId;
    }

    /**
     * @return |null
     */
    public function getExportId() {
        $this->exportId = isset($this->params['exportId']) ? $this->params['exportId'] : null;
        return $this->userId;
    }

    /**
     * 生成
     * @return bool
     * @throws Exception
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\httpclient\Exception
     */
    public function generate() {
        //Logger::info(date('Y-m-d H:i:s'), 'export');
        $this->getParams();
        if (!$this->params) {
            throw new Exception('无法获取需要的参数');
        }
        $path = $this->getSaveBasePath() . __DS__ . $this->getSavePath();
        $path = __DS__ . $path;
        $realFile = $this->mkdir($path) . __DS__ . $this->getFileName();
        $this->completed = false;
        $this->getExportId();
        $this->getUserId();
        $fileNameUpdated = false;
        $this->_fp = fopen($realFile, 'w');
        @exec('chown -R www:www /'.$realFile);
        @exec('chmod -R 777 /'.$realFile);
        // 获取列表数据及表头
        $this->_listModel = new $this->listModelClass();
        $this->_listModel->isExport = true;
        if(method_exists($this->_listModel, 'getExportHeaders')){
            if(method_exists($this->_listModel, 'exportPrepare')) {//导出数据提前设置参数
                $this->_listModel->exportPrepare(['condition' => $this->params['condition'], 'params' => $this->params['params'], 'others' => isset($this->params['others']) ? $this->params['others'] : [], 'page' => 0, 'pageSize' => $this->pageSize, 'userId' => $this->userId]);
            }
            $headers = $this->_listModel->getExportHeaders($this->userId);
            fputcsv($this->_fp, $headers);
            unset($headers);
            $this->_updateFirst($realFile, 1);
        } else {
            throw new Exception($this->listModelClass.'的getExportHeaders方法不存在');
        }
        $this->_loopWriteData($realFile, $fileNameUpdated);
        $this->complete();
        return true;
    }

    /**
     * 循环写入数据
     * @param $realFile
     * @param $fileNameUpdated
     * @param int $page
     * @param int $counter
     * @return string
     * @throws Exception
     */
    private function _loopWriteData($realFile, &$fileNameUpdated, $page = 0, $counter = 0){
        if(method_exists($this->_listModel, 'getList')){
            $list = $this->_listModel->getList(['condition'=>$this->params['condition'], 'params'=>isset($this->params['params']) ? $this->params['params'] : [], 'others'=>isset($this->params['others']) ? $this->params['others'] : [], 'page'=>$page, 'pageSize'=>$this->pageSize, 'userId'=>$this->userId]);
        } else {
            throw new Exception($this->listModelClass.'的getList方法不存在');
        }
        if($page >= $list['totalPages']){
            return 'finish';
        }
        if(!empty($list['list'])){
            foreach($list['list'] as $columnData){
                fputcsv($this->_fp, Tool::arrTextConvert($columnData));
                $counter++;

                if ($list['totalCount'] < $counter) {
//                    $counter = $list['totalCount'];
                    return 'finish';
                }
                $percent = intval(($counter / $list['totalCount']) * 100);
                $this->_updatePercent($percent);
//                if (!$fileNameUpdated) {
//                    $this->_updateFirst($realFile, $percent);
//                } else {
//                    $this->_updatePercent($percent);
//                }
//                $fileNameUpdated = true;
                unset($percent, $columnData);
            }
            $page++;
            unset($list);
            return $this->_loopWriteData($realFile, $fileNameUpdated, $page, $counter);
        }
        unset($list);
        return 'finish';
    }

    /**
     * 完成
     * @return bool
     * @throws Exception
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\httpclient\Exception
     */
    public function complete() {
        $this->_updatePercent(100);
        if ($this->completed) {
            return true;
        }
        if ($this->_fp) {
            fclose($this->_fp);
        }
        // 把导出的文件上传至静态文件服务器
        if ($this->isRemote) {
            $realFile = $this->getSaveBasePath() . __DS__ . $this->getSavePath() . __DS__ . $this->getFileName();
            $remoteUploadApi = RemoteUploadApi::instance();
            if ($uploadResult = $remoteUploadApi->upload($realFile)) {
                Export::updateAll(['REMOTE_URL' => $uploadResult['url'], 'IS_EXPORTING' => 0, 'EXPORT_PERCENT' => 100, 'ENDED_AT' => Date::nowTime()], 'ID=:ID', [':ID' => $this->exportId]);
            } else {
                $this->_errorHandle();
                throw new Exception('文件远程上传失败');
            }
            // 删除本地临时文件
            unlink($realFile);
        } else {
            Export::updateAll(['IS_EXPORTING' => 0, 'EXPORT_PERCENT' => 100, 'ENDED_AT' => Date::nowTime()], 'ID=:ID', [':ID' => $this->exportId]);
        }

        \Yii::$app->swooleAsyncTimer->pushAsyncPercentToAdmin(100, ['MODEL' => 'EXPORT', 'ID' => $this->exportId, 'FIELD' => 'EXPORT_PERCENT']);
        CacheHelper::deleteAsyncParams($this->taskId);
        $this->completed = true;
    }

    /**
     * @param $message
     */
    private function _errorNotice($message) {
        Yii::$app->swooleAsyncTimer->pushAsyncResultToAdmin($this->userId, $message, false);
    }

    /**
     * 首次更新
     * @param $exportId
     * @param $realName
     */
    private function _updateFirst($realName, $percent) {
        $fileName = str_replace($this->getSaveBasePath() . __DS__, '', $realName);
        Export::updateAll([
            'FILE_NAME' => str_replace(__DS__, '/', $fileName),
            'UPDATED_AT' => Date::nowTime(),
            'EXPORT_PERCENT' => $percent,
        ], 'ID=:ID', [':ID' => $this->exportId]);
    }

    /**
     * 更新百分比
     * @param $exportId
     * @param $percent
     */
    private function _updatePercent($percent) {
        // 把数据写入数据库中
        Export::updateAll(['EXPORT_PERCENT' => $percent], 'ID=:ID', [':ID' => $this->exportId]);
        \Yii::$app->swooleAsyncTimer->pushAsyncPercentToAdmin($percent, ['MODEL' => 'EXPORT', 'ID' => $this->exportId, 'FIELD' => 'EXPORT_PERCENT']);
    }

    /**
     * 发生错误处理
     * @param $exportId
     */
    private function _errorHandle() {
        Export::updateAll(['IS_EXPORTING' => 0], 'ID=:ID', [':ID' => $this->exportId]);
    }

    /**
     * 导出逻辑
     * @param $filter
     * @param $listName
     * @param null $consoleRouter
     * @throws Exception
     */
    public function exportHandle($filter, $listName, $consoleRouter = null){
        $params = [
            'moduleId' => $this->moduleId,
            'listName' => $listName,
            'action' => $consoleRouter ? $consoleRouter : Yii::$app->controller->id.'/'.Yii::$app->controller->action->id, // 这里这么写，是因为调用的异步路由和同步的控制器和方法是一样的，所以，只要不传默认调和同步一样的异步方法
            'userId' => Yii::$app->user->id,
        ];
        $this->webToAsync($params,$filter);
    }

    /**
     * 页面到异步的请求
     * @param $params
     * @param array $extend
     * @return bool
     * @throws Exception
     */
    public function webToAsync($params, $extend = []) {
        $default = [
            'moduleId' => null,
            'listName' => null,
            'remark' => null,
            'action' => null,
            'userId' => null,
        ];
        $params = Tool::deepParse($params, $default);
        if (!$params['moduleId'] || !$params['listName'] || !$params['action']) {
            throw new Exception('请设置moduleId,listName和action');
        }

        // 把文件对应的相关资料存入数据库中
        $export = new Export();
        $export->EXPORT_NAME = $params['listName'] . '_' . date('y年m月d日H时i分s秒导出', Date::nowTime());
        $export->MODULE_NAME = $params['moduleId'];
        $export->REMARK = $params['remark'];
        $export->IS_EXPORTING = 1;
        $export->ADMIN_ID = \Yii::$app->user->id;
        $export->STARTED_AT = Date::nowTime();
        $export->CREATED_AT = Date::nowTime();
        $export->EXPORT_PERCENT = 0;
        $export->ENDED_AT = 0;
        $export->UPDATED_AT = 0;
        if (!$export->save()) {
            throw new Exception(Form::formatErrorsForApi($export->getErrors()));
        }
        $extend = array_merge($extend, [
            'exportId' => $export->ID,
            'userId' => $params['userId'],
        ]);
        // 异步处理添加任务
        $taskKey = \Yii::$app->swooleAsyncTimer->asyncHandle($params['action'], $extend);
        if($taskKey === false){
            // 删除刚刚添加的导出数据
            Export::deleteAll(['ID'=>$export->ID]);
            throw new Exception('导出失败，请求异步处理服务器失败');
        }
        return $taskKey;
    }

    /**
     * 生成
     * @return bool
     * @throws Exception
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\httpclient\Exception
     */
    public function generatePDF() {
        $this->getParams();
        if (!$this->params) {
            throw new Exception('无法获取需要的参数');
        }
        $path = __DS__ . $this->getSaveBasePath() . __DS__ . $this->getSavePath();
        $realFile = $path . __DS__ . $this->getFileName('.pdf');

        $this->completed = false;
        $this->getExportId();
        $this->getUserId();
        $fileNameUpdated = false;

        // 获取列表数据及表头
        $this->_listModel = new $this->listModelClass();
        $this->_listModel->isExport = true;

        // 查询订单数据
        $oderList = $this->_loopWriteDataPDF($realFile, $fileNameUpdated);
        if ($oderList) {
            $userId = '';
            $userName = '';
            $address = '';
            $mobile = '';
            $orderAt = '';
            $orderDetails = '';
            $orderSn = '';
            $orderAmount = 0;  // 合计总额
            $orderNums = 0; // 合计总数
            $totalTaxAmount = 0; // 合计税额
            $totalAmount = 0;
            foreach ($oderList as $key => $value) {
                $provinceName = $value['PROVINCE'] ? Region::getCnName($value['PROVINCE']) : '';
                $cityName = $value['CITY'] ? Region::getCnName($value['CITY']) : '';
                $countyName = $value['COUNTY'] ? Region::getCnName($value['COUNTY']) : '';

                $userId = $value['USER_NAME'];
                $userName = $value['CREATE_USER_NAME'];
                $address = $provinceName . $cityName . $countyName . $value['ADDRESS'];
                $mobile = $value['MOBILE'];
                $orderAt = $value['PAY_AT'];
                $orderSn = $value['SN'];
                // 总价
                $totalAmount = $value['BUY_NUMS'] * $value['REAL_PRICE'];
                $orderAmount += $totalAmount;
                $orderNums += $value['BUY_NUMS'];
                // 税额
                $taxAmount = floatval(Tool::formatPrice(($value['REAL_PRICE'] - ($value['REAL_PRICE'] / (1 + $value['TAX_RATE'] / 100))) * $value['BUY_NUMS']));
                $totalTaxAmount += $taxAmount;
                // 订单详情
                $orderDetails .= <<<EOT
                    <tr>
                        <td>{$value['SKU_CODE']}</td>
                        <td>{$value['GOODS_TITLE']}</td>
                        <td>{$value['REAL_PRICE']}</td>
                        <td>{$value['BUY_NUMS']}</td>
                        <td>{$value['TAX_RATE']}</td>
                        <td>{$taxAmount}</td>
                        <td>{$totalAmount}</td>
                    </tr>
EOT;
            }

            // 订单基本信息
            $orderBase = <<<ORDER
                <table border="1" style="table-layout: fixed; padding: 10px 20px;" width="100%">
                    <tr>
                        <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member code</td>
                        <td width="70%">{$userId}</td>
                    </tr>
                    <tr>
                        <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member name</td>
                        <td width="70%">{$userName}</td>
                    </tr>
                    <tr>
                        <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member address</td>
                        <td width="70%">{$address}</td>
                    </tr>
                    <tr>
                        <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member phone</td>
                        <td width="70%">{$mobile}</td>
                    </tr>
                    <tr>
                        <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Order code</td>
                        <td width="70%">{$orderSn}</td>
                    </tr>
                    <tr>
                        <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Creation time</td>
                        <td width="70%">{$orderAt}</td>
                    </tr>
                    <tr>
                        <td class="bg" style="font-weight: bold; font-size: 14px; text-align: center;">Order detail</td>
                        <td class="bg"></td>
                    </tr>
                </table>
ORDER;

            $l['a_meta_charset'] = 'UTF-8';
            $l['a_meta_dir'] = 'ltr';
            $l['a_meta_language'] = 'zh';
            $l['w_page'] = '页面';

            $context = <<<ORDER
                <!doctype html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8" />
                    <title>Order detail</title>
                    <style>
                        table {
                            border-collapse: collapse;
                        }
                        table td, table th {
                            border: 1px solid #ccc;
                            padding: 10px 30px;
                            border-collapse: collapse;
                        }
                        td {
                            padding: 120px;
                        }
                        .bg {
                            background-color: #ccc;
                        }
                    </style>
                </head>
                <body>
                    <div class="content">
                        <p style="text-align: center; font-weight: bold; font-size: 22px;"><b>订单详情</b><br></p>
                        <div>
                            <div style="display: block; width: 100%;">
                                {$orderBase}
                                
                                <table border="1" width="100%" style="padding: 10px 20px; text-align: center;">
                                    <tr>
                                        <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Product code</th>
                                        <th width="20%" style="font-size: 14px; font-weight: bold; text-align: center;">Product name</th>
                                        <th width="13%" style="font-size: 14px; font-weight: bold; text-align: center;">Product price</th>
                                        <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Quantity</th>
                                        <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">Tax rate</th>
                                        <th width="12%" style="font-size: 14px; font-weight: bold; text-align: center;">Tax</th>
                                        <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Total amount</th>
                                    </tr>
                                    {$orderDetails}
                                    <tr>
                                        <td colspan="3">Total</td>
                                        <td>{$orderNums}</td>
                                        <td></td>
                                        <td>{$totalTaxAmount}</td>
                                        <td>{$orderAmount}</td>
                                    </tr>
                                </table>
                            </div>
                            
                            <div style="width: 100%; margin-top: 50px; height: 30px;">
                                <table width="100%" style="border: none; padding: 10px 20px; text-align: center;">
                                    <tr style="border: none;">
                                        <td width="70%" style="border: none;"></td>
                                        <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">Signature：</td>
                                    </tr>
                                    <tr style="border: none;">
                                        <td width="70%" style="border: none;"></td>
                                        <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">Date：</td>
                                    </tr>
                                </table>
                            </div>
                        </div>
                    </div>
                </body>
                </html>
ORDER;

            require_once (\Yii::$app->vendorPath . '/tecnickcom/tcpdf/tcpdf.php');

            $pdf = new \TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
            // 设置打印模式
            $pdf->SetCreator(PDF_CREATOR);
            $pdf->SetAuthor('DaZe');
            $pdf->SetTitle($orderSn);
            $pdf->SetSubject('TCPDF Tutorial');
            $pdf->SetKeywords('TCPDF, PDF, example, test, guide');
            // 是否显示页眉
            $pdf->setPrintHeader(false);
            // 设置页眉字体
            $pdf->setHeaderFont(Array('dejavusans', '', '12'));
            // 页眉距离顶部的距离
            $pdf->SetHeaderMargin('5');
            // 是否显示页脚
            $pdf->setPrintFooter(false);
            // 设置默认等宽字体
            $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
            // 设置行高
            $pdf->setCellHeightRatio(1);
            // 设置左、上、右的间距
            $pdf->SetMargins('10', '10', '10');
            // 设置是否自动分页  距离底部多少距离时分页
            $pdf->SetAutoPageBreak(TRUE, '15');
            // 设置图像比例因子
            $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
            if (@file_exists(\Yii::$app->vendorPath . 'tecnickcom/tcpdf/examples/lang/eng.php')) {
                require_once(\Yii::$app->vendorPath . '/tecnickcom/tcpdf/examples/lang/eng.php');
                $pdf->setLanguageArray($l);
            }
            $pdf->setFontSubsetting(true);
            $pdf->AddPage();
            // 设置字体
            $pdf->SetFont('stsongstdlight', '', 10, '', true);
            $pdf->writeHTML($context);
            $pdf->Output($realFile, 'F');

            $this->_updateFirst($realFile, 1);
        }

        $this->complete();
        return true;
    }

    /**
     * 循环写入数据
     * @param $realFile
     * @param $fileNameUpdated
     * @param int $page
     * @param int $counter
     * @return array
     * @throws Exception
     */
    private function _loopWriteDataPDF($realFile, &$fileNameUpdated, $page = 0, $counter = 0){
        if(method_exists($this->_listModel, 'getList')){
            $list = $this->_listModel->getList(['condition'=>$this->params['condition'], 'params'=>isset($this->params['params']) ? $this->params['params'] : [], 'others'=>isset($this->params['others']) ? $this->params['others'] : [], 'page'=>$page, 'pageSize'=>$this->pageSize, 'userId'=>$this->userId]);
        } else {
            throw new Exception($this->listModelClass.'的getList方法不存在');
        }

        return $list['list'];
    }
}