<?php

namespace Acms\Services\Setup\Fix\Tools;

use Acms\Services\ConfigServer\ConfigServerWriter;
use Acms\Services\Setup\Http\Request;
use Acms\Services\Setup\SetupCommon;
use Acms\Services\Setup\SetupSession;
use Acms\Services\Setup\Tool\SetupTool;
use Acms\Services\Setup\Tool\ToolErrors;
use Acms\Services\Setup\Tool\ToolView;
use Acms\Services\Facades\Database;
use Field;
use SQL;

/**
 * ドメイン修正ツール（fix.php?c=domain）。旧 setup/fix.php の Surfacer_FixDomain。
 * blog_domain を更新し、config.server.php の DOMAIN も書き換える。ログイン不能時の復旧用。
 */
class Domain implements SetupTool
{
    public function template(): string
    {
        return '@setup/fix/domain.twig';
    }

    public function show(Request $request): ToolView
    {
        return ToolView::form($this->formData($request->post()));
    }

    public function handle(Request $request, SetupSession $session): ToolView
    {
        $post = $request->post();
        $data = $this->formData($post);

        $errors = $this->validate($post);
        if (!$errors->isEmpty()) {
            return ToolView::invalid($errors, $data);
        }

        // 検証通過後に DB とconfigを書き換える。config 書き換えに失敗したら（DB は更新済みでも）
        // エラー画面に戻して再試行を促す（旧 business の挙動を踏襲）。
        $errors = $this->apply($post);
        if (!$errors->isEmpty()) {
            return ToolView::invalid($errors, $data);
        }

        return ToolView::done($data);
    }

    /**
     * @return array<string, mixed>
     */
    private function formData(Field $post): array
    {
        return [
            // 入力初期値は現在の DOMAIN。エラー再表示時は submit 値（domain）をテンプレートで使う。
            'currentDomain' => DOMAIN,
            'domain' => $post->get('domain'),
            // all_blog === 'on' で全ブログ書き換えを判定する。チェックボックスは value="on" に統一。
            'allBlog' => $post->get('all_blog') === 'on',
        ];
    }

    private function validate(Field $post): ToolErrors
    {
        $errors = new ToolErrors();

        if ($post->isNull('domain')) {
            $errors = $errors->with('domain_input');
        } elseif (!preg_match(REGEX_VALID_DOMAIN, $post->get('domain'))) {
            $errors = $errors->with('domain_invalid');
        }

        if (!SetupCommon::checkPermission(PATH_CONFIG, '666')) {
            $errors = $errors->with('config_permission');
        }

        return $errors;
    }

    private function apply(Field $post): ToolErrors
    {
        $domain = $post->get('domain');

        $SQL = SQL::newUpdate('blog');
        $SQL->addUpdate('blog_domain', $domain);
        if ($post->get('all_blog') !== 'on') {
            $SQL->addWhereOpr('blog_id', 1);
        }
        Database::query($SQL->get(dsn()), 'exec');

        // config.server.php の DOMAIN を差分更新する（旧 SetupCommon::rebuildConfig の代替）。
        // ConfigServerWriter は該当 define を in-place 更新し、値中の `$` も安全に扱う。
        try {
            if ((new ConfigServerWriter(PATH_CONFIG))->update(['DOMAIN' => $domain]) === []) {
                return (new ToolErrors())->with('rewrite_config');
            }
        } catch (\Throwable $e) {
            return (new ToolErrors())->with('rewrite_config');
        }

        return new ToolErrors();
    }
}
