<?php

declare(strict_types=1);

namespace Acms\Services\View;

use Acms\Services\Facades\Config;
use Acms\Services\Facades\Database as DB;
use Acms\Services\Facades\Entry;
use Acms\Services\Facades\JQuery;
use Acms\Services\Facades\Login;
use Acms\Services\Facades\Preview;
use Acms\Services\Media\UploadSetting;
use Acms\Services\Url\DeliveryUrlRewriter;
use Acms\Services\Url\ManagedDomainRegistry;
use ACMS_RAM;
use Field;
use SQL;

/**
 * acms.js へ渡すクエリ（jsModule）を組み立てる。
 */
class JsModulesBuilder
{
    /**
     * @var ManagedDomainRegistry
     */
    private $managedDomainRegistry;

    /**
     * @var DeliveryUrlRewriter
     */
    private $deliveryUrlRewriter;

    /**
     * @param ManagedDomainRegistry $managedDomainRegistry
     * @param DeliveryUrlRewriter $deliveryUrlRewriter
     */
    public function __construct(ManagedDomainRegistry $managedDomainRegistry, DeliveryUrlRewriter $deliveryUrlRewriter)
    {
        $this->managedDomainRegistry = $managedDomainRegistry;
        $this->deliveryUrlRewriter = $deliveryUrlRewriter;
    }

    /**
     * acms.js のクエリを取得
     *
     * @return array
     */
    public function getJsModules()
    {
        $app = \App::getInstance();
        assert($app instanceof \Acms\Application);
        $Get =& $app->getGetParameter();

        $Session    =& Field::singleton('session');
        $delStorage = $Session->get('webStorageDeleteKey');

        jsModule('offset', DIR_OFFSET);
        jsModule('jsDir', JS_DIR);
        jsModule('themesDir', '/' . DIR_OFFSET . THEMES_DIR);
        jsModule('ARCHIVES_DIR', $this->deliveryUrlRewriter->replaceDeliveryUrl('/' . DIR_OFFSET . ARCHIVES_DIR));
        jsModule('MEDIA_ARCHIVES_DIR', $this->deliveryUrlRewriter->replaceDeliveryUrl('/' . DIR_OFFSET . MEDIA_LIBRARY_DIR));
        jsModule('MEDIA_STORAGE_DIR', MEDIA_STORAGE_DIR);
        jsModule('bid', BID);
        jsModule('aid', AID);
        jsModule('uid', UID);
        jsModule('cid', CID);
        jsModule('eid', EID);
        jsModule('rvid', RVID);
        // ブログが未登録の BID では blogCode() が null を返す。strict_types 下では
        // htmlspecialchars() が TypeError になるため空文字へ倒す。
        jsModule('bcd', htmlspecialchars((string)ACMS_RAM::blogCode(BID), ENT_QUOTES));
        jsModule('rid', $Get->get('rid', null));
        jsModule('mid', $Get->get('mid', null));
        jsModule('setid', $Get->get('setid', null));
        jsModule('layout', LAYOUT_EDIT);
        jsModule('googleApiKey', config('google_api_key'));
        jsModule('jQuery', JQuery::getVersion());
        jsModule('jQueryMigrate', JQuery::getMigrate());
        jsModule('mediaClientResize', config('media_client_resize', 'on'));
        jsModule('delStorage', $delStorage);
        jsModule('fulltimeSSL', (SSL_ENABLE and FULLTIME_SSL_ENABLE) ? 1 : 0);
        jsModule('v', md5(VERSION));
        jsModule('dbCharset', DB_CONNECTION_CHARSET);
        jsModule('auth', getAuthConsideringRole(SUID) ?: '');

        // 公開ページでも使用（built-in/library.js の fileiconPath() 経由）
        jsModule('fileiconDir', '/' . DIR_OFFSET . config('file_icon_dir'));

        // ログイン時のみ必要（メディアアップロード・タイムマシン・インライン編集など）
        if (Login::isLoggedIn()) {
            jsModule('umfs', ini_get('upload_max_filesize'));
            jsModule('pms', ini_get('post_max_size'));
            jsModule('mfu', ini_get('max_file_uploads'));
            jsModule('lgImg', config('image_size_large_criterion') . ':' . preg_replace('/[^0-9]/', '', config('image_size_large')));
            jsModule('jpegQuality', config('image_jpeg_quality', 75));
            jsModule('urlPreviewExpire', config('url_preview_expire'));
            jsModule('timemachinePreviewDefaultDevice', config('timemachine_preview_default_device'));
            jsModule('timemachinePreviewHasHistoryDevice', config('timemachine_preview_has_history_device'));
            jsModule('unitAlignVersion', config('unit_align_version', 'v2'));
            jsModule('mediaLibrary', config('media_library'));
            jsModule('mediaUploadLimit', UploadSetting::normalizeLimit(config('media_upload_limit')));
            jsModule('mediaUploadConcurrency', UploadSetting::normalizeConcurrency(config('media_upload_concurrency')));
            jsModule('edition', LICENSE_EDITION);
            jsModule('entryEditPageType', config('entry_edit_page_type'));
            // config set（ユニットエディターの localStorage キー生成に使用）
            jsModule('configSetId', Config::getCurrentConfigSetId());
            jsModule('themeSetId', Config::getCurrentThemeSetId());
            jsModule('editorSetId', Config::getCurrentEditorSetId());

            // limit（管理画面のエントリー/モジュール一覧件数設定）
            $limitOptions = configArray('admin_limit_option');
            $defaultLimit = $limitOptions[config('admin_limit_default')];
            jsModule('limitOptions', $limitOptions);
            jsModule('defaultLimit', $defaultLimit);

            // ダイレクト編集のためのデータをセットする
            jsModule('editInplace', Entry::isDirectEditEnabled() ? 'on' : 'off');
        }

        if ($Session->get('timemachine_datetime')) {
            jsModule('timeMachineMode', 'true');
        }

        // プレビュー / タイムマシンの DeviceMode（管理画面側）と、プレビュー対象ページ側の
        // window.postMessage の origin 検証専用。必要な3つのレンダーコンテキストでのみ公開する:
        // 通常の管理画面（Login::isLoggedIn）、プレビュー対象として表示中のページ（Preview::isPreviewMode）、
        // 未ログイン第三者が開くプレビュー共有ラッパーページ（Preview::isPreviewShareAdmin）。
        if (
            Login::isLoggedIn()
            || Preview::isPreviewMode()
            || (defined('ADMIN') && Preview::isPreviewShareAdmin(ADMIN))
        ) {
            jsModule('previewAllowedOrigins', $this->managedDomainRegistry->getManagedOrigins());
        }
        if (sessionWithAdministration()) {
            jsModule('rootTpl', ROOT_TPL);
        }
        if (defined('IS_EDITING_ENTRY') && IS_EDITING_ENTRY) {
            $Session->delete('webStorageDeleteKey');
        }

        //--------------
        // multi domain
        jsModule('multiDomain', '0');
        if (defined('LICENSE_OPTION_PLUSDOMAIN') && intval(LICENSE_OPTION_PLUSDOMAIN) > 0) {
            $SQL = SQL::newSelect('blog');
            $SQL->setSelect(SQL::newFunction('blog_domain', 'DISTINCT'), 'domains', null, 'COUNT');
            $domain_num = DB::query($SQL->get(dsn()), 'one');
            if (intval($domain_num) > 1) {
                jsModule('multiDomain', '1');
            }
        }

        //----------
        // category
        if ($cid = CID) {
            $ccds   = [ACMS_RAM::categoryCode($cid)];
            while ($cid = ACMS_RAM::categoryParent($cid)) {
                if ('on' == ACMS_RAM::categoryIndexing($cid)) {
                    $ccds[] = htmlspecialchars((string)ACMS_RAM::categoryCode($cid), ENT_QUOTES);
                }
            }
            jsModule('ccd', join('/', array_reverse($ccds)));
        }

        //---------
        // session
        jsModule('admin', ADMIN);
        jsModule('rid', RID);
        jsModule('ecd', ACMS_RAM::entryCode(EID));
        jsModule('keyword', htmlspecialchars(str_replace('　', ' ', KEYWORD), ENT_QUOTES));
        jsModule('scriptRoot', '/' . DIR_OFFSET . (REWRITE_ENABLE ? '' : SCRIPT_FILENAME . '/'));

        //-------
        // cache
        if (config('javascript_nocache') === 'on') {
            jsModule('cache', uniqueString());
        }

        // url segments（JS側の AcmsPathSegments 型が必要とするキーのみに絞る）
        $jsSegmentKeys = [
            'bid', 'cid', 'eid', 'uid', 'utid',
            'tag', 'field', 'span', 'page', 'order',
            'limit', 'keyword', 'admin', 'tpl', 'api',
        ];
        jsModule('segments', array_intersect_key(getRoutingSegments(), array_flip($jsSegmentKeys)));

        // 管理画面のみ必要（キャッシュが効くページでは利用できないため、ログイン済みかつ管理ページのみ）
        if (Login::isLoggedIn() && Login::isAuthRequiredPage()) {
            jsModule('suid', SUID);
            jsModule('sbid', SBID);
        }

        // debug mode（built-in/library.js の isDebugMode() 経由で公開ページでも使用。サードパーティテーマ互換性）
        jsModule('isDebugMode', isDebugMode() ? '1' : '0');

        $jsModules  = [];
        foreach (jsModule() as $key => $value) {
            if ($key === 'domains') {
                $value = implode(',', $value);
            }
            $jsModules[$key] = $value;
        }

        return $jsModules;
    }
}
