<?php

/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2022/1/12
 * Time: 14:15
 */

namespace app\common\service;

use app\common\model\Article;
use app\common\model\ArticleCate;
use app\common\model\Catalog;
use app\common\model\Templet;
use app\common\model\Webconfig;
use file\DirHelper;
use file\FileHelper;
use file\PathHelper;
use paginate\Bootstrap;
use think\Controller;
use think\Log;
use think\Request;

class CmsService extends Controller
{

    /**
     * 输出调试信息
     * @param $msg
     */
    public function info($msg)
    {
        if ($this->request->isCli()) {
            echo $msg . PHP_EOL;
        } else {
            Log::log($msg);
        }
    }

    /**
     * 记录错误信息日志
     * @param $e \Exception
     */
    public static function errorlog($e)
    {
        Log::error("异常信息:" . $e->getMessage());
        Log::error('请求数据:' . var_export(Request::instance()->param(), true));
        Log::error($e->getTraceAsString());
    }


    /**
     * 输出错误页面
     * @param $basePath
     * @return string
     */
    public static function errorHtml($basePath)
    {
        $basePath = str_replace(Templet::getRootDir(), '', $basePath);
        return "<html><head><meta charset=\"utf-8\"></head><body>" . $basePath . "，此栏目下没有文章，请添加文章后再尝试生成</body></html>";
    }

    /**
     * 替换预览内容
     * @return array
     */
    public static function getArticlePerViewReplace()
    {
        return [
            '_UPLOADS_' => '/uploads/q1464674022',
            '_ROOT_' => '/?path=',
        ];
    }

    /**
     * 替换生成内容
     * @return array
     */
    public static function getArticleReplace()
    {
        return [
            '_UPLOADS_' => '',
            '_ROOT_' => '',
        ];
    }

    /**
     * 导出上传目录
     */
    public function copyUploads()
    {
        //创建目录
        $destPath = Templet::getRootDir() . 'uploads';
        DirHelper::makeDir($destPath);
        $this->info("导出上传目录" . $destPath);

        $path = Templet::UPLOAD_PATH . 'admin';
        if (file_exists($path)) {
            DirHelper::copyDir($path, $destPath . DS . 'admin', false);//不覆盖同名文件
        }

        $path = Templet::UPLOAD_PATH . 'ueditor';
        if (file_exists($path)) {
            DirHelper::copyDir($path, $destPath . DS . 'ueditor', false);//不覆盖同名文件
        }

        //导出模板文件
//        $path = APP_PATH . 'admin' . DS . 'view' . DS . 'q1464674022';
//        if (file_exists($path)) {
//            copyDir($path, $destPath . DS . 'template');
//        }
    }

    /**
     * 执行文件生成
     * @param $filePath
     * @param $content
     */
    public function createFile($filePath, $content)
    {
        if (!ifContain($filePath, Templet::getRootDir())) {
            return;//不允许执行外部路径
        }
        if ($this->request->has('delete')) {
            if (file_exists($filePath)) {
                unlink($filePath);//删除
            }
        } elseif ($this->request->has('cover')) {
            FileHelper::save($filePath, $content);//覆盖
        } else {
            //默认不覆盖,文件不存在或内容修改才覆盖
            if (!file_exists($filePath)) {
                FileHelper::save($filePath, $content);
            } else {
                $change = str_replace(file_get_contents($filePath), '', $content);
                if (!empty($change)) {
                    FileHelper::save($filePath, $content);
                }
            }
        }
    }

    /**
     * 分页url处理
     * @param $catalog
     * @param $tid
     * @return mixed
     */
    public static function getPaginatePath($catalog, $tid)
    {
        $rule = str_replace("{page}", "[PAGE]", $catalog->articlelist_rule);
        return replaceUrlDS(ArticleCate::getRulePath($catalog->path, $rule, $catalog->id, $tid));
    }

    /**
     * 栏目预览
     * @param $catalog Catalog
     * @param $cate ArticleCate
     * @param int $page
     * @return mixed|string
     */
    public function perviewCatalog($catalog, $cate = null, $page = 1)
    {
        $tid = $cate ? $cate->id : 0;
        if ($catalog) {
            try {
                $replace = self::getArticlePerViewReplace();

                //先预览静态文件
//                $filepath = Templet::getRootDir() . $catalog->getUri();
//                if (file_exists($filepath) && ifContain($filepath, '.html')) {
//                    return $this->fetch($filepath, [], $replace);
//                }

                //全局对象
                $cataLogModel = new Catalog();
                $articleModel = new Article();
                $articleCateModel = new ArticleCate();
                $this->assign([
                    'cataLogModel' => $cataLogModel,
                    'articleCateModel' => $articleCateModel,
                    'articleModel' => $articleModel,
                ]);

                if ($catalog->type == Catalog::TYPE_CATALOG) {

                    return $this->fetch($catalog->getCatalogTemplet()->getRealPath(), ['catalog' => $catalog], $replace);

                } elseif ($catalog->type == Catalog::TYPE_ARTICLE_LIST) {

                    if ($catalog->article_cate_count > 0) {
                        $where = [
                            'article_cate_id' => $tid,
                            'status' => $articleModel::STATUS_OPEN,
                        ];
                    } else {
                        $where = [
                            'catalog_id' => $catalog->id,
                            'status' => $articleModel::STATUS_OPEN,
                        ];
                    }

                    $total = $articleModel->where($where)->where($catalog->articlelist_where)->count();
                    $listRows = $catalog->articlelist_rows??10;//分页
                    $orderby = $catalog->articlelist_sort??'create_time desc';//排序
                    $prefix = isset($replace['_ROOT_']) ? $replace['_ROOT_'] : '_ROOT_';
                    $articleList = $articleModel->where($where)->where($catalog->articlelist_where)->page($page, $listRows)->order($orderby)->select();
                    $paginate = new Bootstrap($articleList, $listRows, $page, $total, false, ["path" => $prefix . self::getPaginatePath($catalog, $tid)]);
                    return $this->fetch($catalog->getCatalogTemplet()->getRealPath(), ['catalog' => $catalog, 'articleCate' => $cate, 'paginate' => $paginate], $replace);

                }
                return "";
            } catch (\Exception $e) {
                $this->error('预览失败:' . $e->getMessage());
            }
        }
        abort(404, "预览失败:catelog is null");
    }


    /**
     * 分类预览
     * @param $articleCate ArticleCate
     * @param int $page
     * @return mixed|string
     */
    public function perviewArticleCate($articleCate, $page = 1)
    {
        if ($articleCate) {
            try {
                //先预览静态文件
//                $filepath = Templet::getRootDir() . $articleCate->getUri();
//                if (file_exists($filepath)) {
//                    return $this->fetch($filepath, [], $replace);
//                }

                $catalog = $articleCate->getFirstCatalog();
                return $this->perviewCatalog($catalog, $articleCate, $page);
            } catch (\Exception $e) {
                $this->error('预览失败:' . $e->getMessage());
            }
        }
        abort(404, "预览失败:articleCate is null");
    }

    /**
     * 文章预览
     * @param $article Article
     * @return mixed
     */
    public function perviewArticle($article)
    {
        if ($article) {
            try {
                $replace = self::getArticlePerViewReplace();

                //先预览静态文件
//               $filepath = Templet::getRootDir() . $article->getUri();
//               if (file_exists($filepath) && ifContain($filepath, '.html')) {
//                   return $this->fetch($filepath, [], $replace);
//              }

                //全局对象
                $cataLogModel = new Catalog();
                $articleModel = new Article();
                $articleCateModel = new ArticleCate();
                $this->assign([
                    'cataLogModel' => $cataLogModel,
                    'articleCateModel' => $articleCateModel,
                    'articleModel' => $articleModel,
                ]);

                $catalog = $article->catalog_id ? $article->catalog : $article->getFirstCatalog();
                $cate = $article->catalog_id ? null : $article->cate;
                /** @var Catalog $catalog */
                return $this->fetch($catalog->getArticleTemplet()->getRealPath(), ['catalog' => $catalog, 'article' => $article, 'articleCate' => $cate], $replace);
            } catch (\Exception $e) {
                $this->error('预览失败:' . $e->getMessage());
            }
        }
        abort(404, "预览失败:article is null");
    }

    /**
     * 预览网站静态文件
     * @return string
     */
    public function perviewHtmlSite()
    {
        if ($this->request->has('path')) {
            $filepath = Templet::getRootDir() . $this->request->get('path');
            if (!ifContain($filepath, '.html')) {
                $filepath .= DS . 'index.html';
            }
        } else {
            $filepath = Templet::getRootDir() . 'index.html';
        }

        if (file_exists($filepath)) {
            $replace = self::getArticlePerViewReplace();
            $html = $this->fetch($filepath, [], $replace);
            //todo auto replace assets path
            exit($html);
        } else {
            if ($this->request->has('path'))
                abort(404);
        }
    }

    /**
     * 预览网站模板
     */
    public function perviewSite()
    {
        $path = $this->request->get('path', '/index.html');
        if (!ifContain($path, '.html')) {
            $path .= '/index.html';
        }
        $path = str_replace('//', '/', appendStartDS($path, '/'));

        $catalogModel = new Catalog();
        $catalog = $catalogModel->where('path', $path)->find();
        if ($catalog) {
            exit($this->perviewCatalog($catalog));
        }

        $path = deleteStartDS($path, '/');

        $catalogs = $catalogModel->field('article_rule,articlelist_rule')->distinct(true)->where('article_rule', '<>', '')->select();
        foreach ($catalogs as $catalog) {
            $matches = $this->matchePath($catalog->article_rule, $path);

            if ($matches) {
                if (isset($matches['{aid}'])) {
                    $article = Article::get($matches['{aid}']);
                    exit($this->perviewArticle($article));
                }
            }
            $matches = $this->matchePath($catalog->articlelist_rule, $path);
            if ($matches) {
                if (isset($matches['{tid}'])) {
                    $articlecate = (new ArticleCate())->where('id', $matches['{tid}'])->find();
                } elseif (isset($matches['{tpath}'])) {
                    $articlecate = (new ArticleCate())->where('en_name', $matches['{tpath}'])->find();
                } else {
                    continue;
                }
                $page = $matches['{page}'] ?? 1;
                exit($this->perviewArticleCate($articlecate, $page));
            }
        }
    }

    /**
     * 解析path
     * @param $rule
     * @param $path
     * @return mixed
     */
    private function matchePath($rule, $path)
    {
        $rule_arr = explode('/', $rule);
        $path_arr = explode('/', $path);

        if (ifContain($path, 'index.html')) {
            array_pop($rule_arr);
            array_pop($path_arr);
            $rule = implode('/', $rule_arr);
            $path = implode('/', $path_arr);
        }

        $pattern2p = str_replace(['/', '{cpath}', '{tpath}', '{Y}', '{M}', '{D}', '{aid}', '{page}', '{cid}', '{tid}'], ['\/', '(.*?)', '(.*?)', '(\d{4})', '(\d{2})', '(\d{2})', '(\d*?)', '(\d*?)', '(\d*?)', '(\d*?)'], $rule);
        $pattern2r = str_replace(['/', '{cpath}', '{tpath}', '{Y}', '{M}', '{D}', '{aid}', '{page}', '{cid}', '{tid}'], ['\/', '({cpath})', '({tpath})', '({Y})', '({M})', '({D})', '({aid})', '({page})', '({cid})', '({tid})'], $rule);

        if (preg_match('/^' . $pattern2p . '$/', $path, $matches2p)) {
            if (preg_match('/^' . $pattern2r . '$/', $rule, $matches2r)) {
                $res = [];
                foreach ($matches2r as $i => $item) {
                    if ($i > 0)
                        $res[$item] = $matches2p[$i];
                }
                return $res;
            }
        }
    }

}