<?php

ignore_user_abort(false);

require dirname(__FILE__) . '/lib/common.php';

/**
 * path
 */
define('PATH_CONFIG', SetupCommon::fixPathSeparator(SCRIPT_DIR . 'config.server.php'));
define('PATH_LICENSE', SetupCommon::fixPathSeparator(SCRIPT_DIR . 'license.php'));
define('PATH_HTACCESS', SetupCommon::fixPathSeparator(SCRIPT_DIR . '.htaccess'));
define('PATH_ENV', SetupCommon::fixPathSeparator(SCRIPT_DIR . '.env'));
define('PATH_ARCHIVES', SetupCommon::fixPathSeparator(SCRIPT_DIR . ARCHIVES_DIR));
define('PATH_MEDIA', SetupCommon::fixPathSeparator(SCRIPT_DIR . MEDIA_LIBRARY_DIR));
define('PATH_STORAGE', SetupCommon::fixPathSeparator(SCRIPT_DIR . MEDIA_STORAGE_DIR));
define('PATH_THEMES', SetupCommon::fixPathSeparator(SCRIPT_DIR . THEMES_DIR));
define('REGEX_VALID_DOMAIN', '@^([a-zA-Z0-9]+[a-zA-Z0-9-]*(?<=[a-zA-Z0-9])\.)+[a-zA-Z]+[a-zA-Z0-9-]*(?<=[a-zA-Z0-9])\.?$|^(?:\d{1,3}\.){3}\d{1,3}$|^localhost$@'); // phpcs:ignore

if (!defined('CACHE_DIR')) {
    define('CACHE_DIR', 'cache/');
}
define('PATH_CACHE', SetupCommon::fixPathSeparator(SCRIPT_DIR . CACHE_DIR));



$own = preg_replace('@^.*/(.*)$@si', '$1', SetupCommon::fixPathSeparator(dirname($_SERVER['SCRIPT_FILENAME'])));
if (!defined('SETUP_DIR')) {
    define('SETUP_DIR', $own);
}

$baseUrl = (HTTPS ? 'https' : 'http') . '://' . HTTP_HOST . (!!HTTP_PORT ? ':' . HTTP_PORT : '') . '/';
define('HTTP_ROOT', rtrim($baseUrl . DIR_OFFSET, '/') . '/');
$root = SetupCommon::fixPathSeparator(preg_replace('@' . SETUP_DIR . '$@', '', dirname($_SERVER['SCRIPT_FILENAME'])));
define('ROOT_DIR', $root);

class Setup
{
    protected $Post;
    protected $Get;

    public function __construct()
    {
        $this->initialize();
    }

    private function initialize()
    {
        try {
            $DB = DB::singleton(dsn());

            $q  = "SET SESSION sql_mode=''";
            $DB->query($q, 'exec');
        } catch (Exception $e) {
            App::deleteExceptionStack();
        }

        $this->initSession();
        setcookie(SESSION_NAME, '', time() - 1, '/', $_SERVER['HTTP_HOST'], false, true);

        // initialize SEPARATOR
        if (!defined('DIRECTORY_SEPARATOR')) {
            if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
                define('DIRECTORY_SEPARATOR', '\\');
            } else {
                define('DIRECTORY_SEPARATOR', '/');
            }
        }

        // initialize EOL
        if (!defined('PHP_EOL')) {
            $env = strtoupper(substr(PHP_OS, 0, 3));
            switch ($env) {
                case 'WIN':
                    define('PHP_EOL', "\r\n");
                    break;
                case 'MAC':
                    define('PHP_EOL', "\r");
                    break;
                default:
                    define('PHP_EOL', "\n");
                    break;
            }
        }

        // initialize POST
        $this->Post = App::getPostParameter();
        // initialize GET
        $this->Get = App::getGetParameter();
    }

    private function initSession()
    {
        session_name('acmsSetup');
        session_set_cookie_params(0);
        session_start();
        session_regenerate_id(true);
    }

    protected function checkAuth()
    {
        return !empty($_SESSION['auth']) ? true : false;
    }

    protected function isInstalled()
    {
        try {
            $dsn = dsn();
            $DB = DB::singleton($dsn);

            if (empty($dsn['name'])) {
                return false;
            }

            $SQL = SQL::newSelect('blog');
            $SQL->addWhereOpr('blog_id', 1);

            if ($DB->query($SQL->get(dsn()), 'row')) {
                return true;
            }

            App::checkException();
        } catch (Exception $e) {
            App::deleteExceptionStack();
        }

        return false;
    }
}

class Setup_Detector extends Setup
{
    public function __construct()
    {
        parent::__construct();
        $Tpl = SetupCommon::setTpl(dirname(__FILE__) . '/tpl/index.html');

        $error = [];

        /**
         * check installed system
         */
        if (!$this->isInstalled()) {
            header('Location: ./install.php');
            die();
        }

        /**
         * try login
         */
        if (!empty($this->Post->get('id')) && !empty($this->Post->get('pass')) && empty($_SESSION['auth'])) {
            if ($row = $this->loginAuth($this->Post->get('id'), $this->Post->get('pass'))) {
                $_SESSION['auth'] = $row;
            } else {
                $error[] = 'login_auth';
            }
        }

        /**
         * logout
         */
        if ($this->Post->get('action') == 'logout') {
            $_SESSION = [];
            $params = session_get_cookie_params();
            if (version_compare(PHP_VERSION, '7.3.0', '>=')) {
                setcookie(session_name(), '', [
                    'expires' => time() - 42000,
                    'path' => $params["path"],
                    'domain' =>  $params["domain"],
                    'secure' => $params["secure"],
                    'httponly' => $params["httponly"],
                    'samesite' => $params["samesite"],
                ]);
            } else {
                setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"]);
            }
            session_destroy();
            header('Location: ./index.php');
            die();
        }

        /**
         * detect root block
         */
        if (!empty($_SESSION['auth'])) {
            $root = 'admin';

            if (!$this->detectUpdate($Tpl, $root)) {
                $this->detectImport($Tpl, $root);
                $this->detectExport($Tpl, $root);
            }

            $this->detectFixDomain($Tpl, $root);
            $this->detectFixCommentAlign($Tpl, $root);
            $this->detectFixCategoryAlign($Tpl, $root);
            $this->detectFixBlogAlign($Tpl, $root);
            $this->detectFieldSearch($Tpl, $root);
            $this->detectFixAccess($Tpl, $root);
            $this->detectFixIndex($Tpl, $root);
        } else {
            $root = 'curtain';
        }

        /**
         * output error log
         */
        foreach ($error as $key => $val) {
            if (!!is_int($key)) {
                $Tpl->add([$val . ':error', $root]);
            } else {
                $Tpl->add([$val . ':error', $key, $root]);
            }
        }
        $Tpl->add($root);
        print SetupCommon::getTpl($Tpl);
    }

    protected function detectUpdate(&$Tpl, $root)
    {
        $DB = DB::singleton(dsn());
        $SQL = SQL::newSelect('sequence');
        $SQL->addSelect('sequence_system_version');
        $version = $DB->query($SQL->get(dsn()), 'one');

        if (version_compare($version, VERSION, '<')) {
            $Tpl->add(['update', $root]);
            return true;
        } elseif (version_compare($version, VERSION, '=')) {
            $Tpl->add(['current', $root]);
            return false;
        } elseif (version_compare($version, VERSION, '>')) {
            header('Content-type: text/html; charset=UTF-8');
            die("<p>Setupプログラムがデータベースのバージョンよりも古い可能性があります。最新のsetupに差し替えてから実行してください。(Setup: " . VERSION . ", Database: $version )</p>"); // phpcs:ignore
        } else {
            $Tpl->add(['update', $root]);
            return true;
        }
    }

    protected function detectImport(&$Tpl, $root)
    {
        $Tpl->add(['import', $root]);
    }

    protected function detectExport(&$Tpl, $root)
    {
        $Tpl->add(['export', $root]);
    }

    protected function detectFixDomain(&$Tpl, $root)
    {
        $Tpl->add(['fix_domain', $root]);
    }

    protected function detectFixCommentAlign(&$Tpl, $root)
    {
        $Tpl->add(['fix_comment-align', $root]);
    }

    protected function detectFixCategoryAlign(&$Tpl, $root)
    {
        $Tpl->add(['fix_category-align', $root]);
    }

    protected function detectFixBlogAlign(&$Tpl, $root)
    {
        $Tpl->add(['fix_blog-align', $root]);
    }

    protected function detectFieldSearch(&$Tpl, $root)
    {
        $Tpl->add(['fix_field-search', $root]);
    }

    protected function detectFixAccess(&$Tpl, $root)
    {
        $Tpl->Add(['fix_access', $root]);
    }

    protected function detectFixIndex(&$Tpl, $root)
    {
        $Tpl->add(['fix_index'], $root);
    }

    protected function loginAuth($id, $pass)
    {
        $DB = DB::singleton(dsn());
        $SQL = SQL::newSelect('user');
        $SQL->addWhereOpr('user_auth', 'administrator');
        $W  = SQL::newWhere();
        $W->addWhereOpr('user_code', $id, '=', 'OR');
        $W->addWhereOpr('user_mail', $id, '=', 'OR');
        $SQL->addWhere($W);

        $all = $DB->query($SQL->get(dsn()), 'all');
        $all = array_filter($all, function ($user) use ($pass) {
            return acmsUserPasswordVerify($pass, $user['user_pass'], getPasswordGeneration($user));
        });
        if (empty($all)) {
            return false;
        }
        $user = $all[0];
        return $user['user_id'];
    }
}

class Surfacer extends Setup
{
    public $Error; // is_array
    public $Notice; // is_array

    public function __construct($tplPath)
    {
        parent::__construct();
        if (!$this->checkAuth()) {
            header('Content-type: text/html; charset=UTF-8');
            die("メンテナンス実行に対する権限がありません。管理者はメンテナンスメニューからログインし直してください。");
        }
        $Tpl  = SetupCommon::setTpl($tplPath);

        /**
         * if post data exists. when try to validation.
         */
        if (!empty($_POST)) {
            $this->validate();
        }

        /**
         * if current step is *result*. when try to run business logic.
         */
        if ($this->Post->get('step') === 'result') {
            $this->business();
        }
        $root = 'step#' . $this->Post->get('step', 'apply');
        $this->buildSurface($Tpl, $root);
        $this->buildError($Tpl, $root);
        $Tpl->add($root, Tpl::buildField($this->Post, $Tpl, [$root]));

        print SetupCommon::getTpl($Tpl);
    }

    public function detectStep()
    {
        $step = empty($this->Error) ? 'result' : 'error';
        $this->Post->set('step', $step);
    }

    protected function buildError(&$Tpl, $root)
    {
        if (!empty($this->Error)) {
            foreach ($this->Error as $key => $val) {
                if (is_array($val)) {
                    foreach ($val as $k => $v) {
                        $loop = [$key . ':loop', $root];
                        $Tpl->add($loop, [$key => $k]);
                    }
                }
                $error = [$key . ':error', $root];
                $Tpl->add($error);
            }
            return true;
        } else {
            return false;
        }
    }

    protected function listBlog(&$Tpl, $root)
    {
        /**
         * list blog
         */
        $DB = DB::singleton(dsn());
        $SQL = SQL::newSelect('blog');
        $all = $DB->query($SQL->get(dsn()), 'all');

        foreach ($all as $row) {
            $vars = [
                'bid'    => $row['blog_id'],
                'name'   => $row['blog_name'],
                'code'   => $row['blog_code'],
                'parent' => $row['blog_parent'],
            ];
            if (intval($row['blog_id']) === intval($this->Post->get('bid'))) {
                $vars += ['checked' => ' checked="checked"'];
            }

            $Tpl->add(['blog:loop', $root], $vars);
        }
    }

    public function buildSurface(&$Tpl, $root)
    {
    }

    public function validate()
    {
    }

    public function business()
    {
    }
}
