<?php
/**
 * DBShop 电子商务系统
 *
 * ==========================================================================
 * @link      https://www.dbshop.net/
 * @copyright 北京珑大钜商科技有限公司，并保留所有权利。
 * @license   https://www.dbshop.net/license.html License
 * ==========================================================================
 *
 * @author    静静的风 <baron@loongdom.cn>
 *
 */

namespace Admin\Controller;

use Admin\Data\Common;
use Doctrine\ORM\EntityManager;
use Laminas\Filter\Decompress;
use Laminas\Mvc\I18n\Translator;
use Laminas\View\Model\JsonModel;

class UpdateController extends AdminActionController
{
    private $translator;
    private $entityManager;

    public function __construct(
        Translator      $translator,
        EntityManager   $entityManager
    )
    {
        $this->translator       = $translator;
        $this->entityManager    = $entityManager;
    }

    /**
     * 更新包列表
     * @return array|\Laminas\Http\Response|\Laminas\View\Model\ViewModel
     */
    public function indexAction()
    {
        $result = $this->adminCommon()->dbshopApiService('updatePackageList');
        if ($result == null) return $this->redirect()->toRoute('service-bind');

        return ['result' => $result];
    }

    /**
     * 获取更新包详情
     * @return array|\Laminas\Http\Response
     */
    public function updatePackageInfoAction()
    {
        $packageId  = (int) $this->params()->fromRoute('id');
        $result     = $this->adminCommon()->dbshopApiService('updatePackageInfo', ['id' => $packageId]);
        if (
            !$result
            || ($result['code'] == 200 && empty($result['result']))
            || $result['code'] == 400
        ) return $this->redirect()->toRoute('update');

        return ['result' => $result['result']];
    }

    /**
     * 更新执行
     * @return JsonModel
     * @throws \Doctrine\DBAL\DBALException
     */
    public function startShopPackageAction()
    {
        $packageId  = (int) $this->params()->fromRoute('id');
        $result     = $this->adminCommon()->dbshopApiService('updatePackageInfo', ['id' => $packageId]);
        if (
            !$result
            || ($result['code'] == 200 && empty($result['result']))
            || $result['code'] == 400
        ) return new JsonModel(['state' => 'false', 'message' => $this->translator->translate('更新包信息错误，无法进行更新。')]);

        $packageInfo    = $result['result']['packageInfo'];
        $packagePath    = 'data/moduleData/Package';
        $packageDirName = substr($packageInfo['packageName'], 0, -4);//之所以用substr，防止后缀大小写，直接截取
        $packageZipFile = $packagePath . '/' .$packageInfo['packageName'];
        $sourcePath     = $packagePath.'/'.$packageDirName;

        $downState = $this->adminCommon()->dbshopDownloadPackage('downloadUpdatePackage', ['id' => $packageId], $packageZipFile);
        if (is_string($downState) && $downState == 'keyError') return new JsonModel(['state' => 'false', 'message' => $this->translator->translate('您的网址无法从公网正常访问，不能进行继续操作。')]);
        if (!$downState) return new JsonModel(['state' => 'false', 'message' => $this->translator->translate('更新包下载失败，无法更新。')]);
        if (md5_file($packageZipFile) != strtolower($packageInfo['packageMd5'])) {
            @unlink($packageZipFile);
            return new JsonModel(['state' => 'false', 'message' => $this->translator->translate('下载的更新包与原始更新包不匹配，无法更新。')]);
        }

        $decompress = new Decompress(['adapter' => 'Zip', 'options' => ['target' => $packagePath]]);
        $decompress->filter($packageZipFile);

        //检查权限
        $allUpdateFiles = Common::getAllFile($sourcePath);
        if (empty($allUpdateFiles)) return new JsonModel(['state' => 'false', 'message' => $this->translator->translate('无可更新文件。')]);
        $checkString    = '';
        foreach ($allUpdateFiles as $updateFile) {
            $coverFile  = ltrim(str_replace($sourcePath, '', $updateFile), DIRECTORY_SEPARATOR);
            $dirPath    = dirname($coverFile);
            if (file_exists($coverFile)) {
                if (!is_writable($coverFile)) $checkString .= $coverFile . '&nbsp;[<span class="text-red">' . $this->translator->translate('无写入权限') . '</span>]<br>';
            } else {
                if (!is_dir($dirPath)) @mkdir($dirPath, 0777, true);
                if (!is_writable($dirPath)) $checkString .= $dirPath . '&nbsp;[<span class="text-red">' . $this->translator->translate('无写入权限') . '</span>]<br>';
            }
        }
        if (!empty($checkString)) return new JsonModel(['state' => 'false', 'message' => $checkString]);

        //进行覆盖
        foreach ($allUpdateFiles as $updateFile) {
            $coverFile  = ltrim(str_replace($sourcePath, '', $updateFile), DIRECTORY_SEPARATOR);
            if (!copy($updateFile, $coverFile)) return new JsonModel(['state' => 'false', 'message' => sprintf($this->translator->translate('无法正常更新文件，请检查%s对应的目录是否有相关权限'), $coverFile)]);
            @chmod($coverFile, 0755);
        }

        //检查是否有更新sql文件，如果有则导入sql文件内容，sql文件必须utf8编码的
        if (file_exists($sourcePath . '/sql/update.sql')) {
            $updateSql = str_replace(["\r\n", "\r"], "\n", file_get_contents($sourcePath . '/sql/update.sql'));
            $this->entityManager->getConnection()->prepare($updateSql)->execute();
        }

        //删除更新包及更新文件
        @unlink($packageZipFile);
        Common::deleteDirAndFile($sourcePath);

        //系统更新版本信息
        $shopVersion = "<?php\n";
        if (defined('DBSHOP_WEBSITE_URL')) $shopVersion .= "define('DBSHOP_WEBSITE_URL', '".DBSHOP_WEBSITE_URL."');\n";
        if (defined('DBSHOP_WEBSITE_NAME')) $shopVersion .= "define('DBSHOP_WEBSITE_NAME', '".DBSHOP_WEBSITE_NAME."');\n";
        $shopVersion .= "define('DBSHOP_VERSION', '".$packageInfo['versionName']."');\n";
        $shopVersion .= "define('DBSHOP_VERSION_NUMBER', ".$packageInfo['versionNumber'].");\n";
        file_put_contents('data/shopVersion.php', $shopVersion);

        //更新缓存
        Common::deleteDirAndFile('data/cache', false);
        Common::opcacheReset();

        $this->adminCommon()->addOperLog(sprintf($this->translator->translate('更新包 %s 更新完成!如果还有其他更新包，请继续更新。'), $packageInfo['packageUpdateName']), $this->translator->translate('系统更新'));

        return new JsonModel(['state' => 'true', 'message' => $this->translator->translate('更新完成')]);
    }
}