<?php

try {
    require_once(dirname(__FILE__) . '/application.php');
    require_once(dirname(__FILE__) . '/setup.php');
    require_once(dirname(__FILE__) . '/lib/schema.php');

    $tplPath = dirname(__FILE__) . "/tpl/update.html";

    @set_time_limit(0);

    ini_set("display_errors", 1);
    error_reporting(E_ALL);

    class Surfacer_Update extends Surfacer
    {
        public function buildSurface(&$Tpl, $root)
        {
            if ($root === 'step#drop' || $root === 'step#drop:error') {
                /**
                 * build unusing columns list
                 */
                $Schema = new Schema(dsn(), DB_NAME, DB_PREFIX);
                $Schema->defSetYaml('schema');

                $tbs = Schema::listUp($Schema->schema);
                $dropTables = array_reduce(
                    $tbs,
                    function ($dropTables, $tb) use ($Schema) {
                        $disusedColumns = $Schema->disusedColumns($tb);
                        if (empty($disusedColumns)) {
                            return $dropTables;
                        }
                        return array_merge(
                            $dropTables,
                            [$tb => $disusedColumns]
                        );
                    },
                    []
                );

                if (!empty($dropTables)) {
                    foreach ($dropTables as $table => $columns) {
                        if (empty($columns)) {
                            continue;
                        }
                        foreach ($columns as $col) {
                            $Tpl->add(
                                ['column:loop', 'table:loop', $root],
                                ['column' => $col, 'table' => $table]
                            );
                        }
                        $Tpl->add(['table:loop', $root], ['table' => $table]);
                    }
                } else {
                    $Tpl->add(['notFound:veil', $root]);
                }
            } else {
                /**
                 * check user privileges
                 */
                if (!SetupCommon::checkAlterPerm()) {
                    $this->Error['alter_privilege'] = true;
                    $this->Post->set('mysqlErr', DB::errorCode() . ': ' . DB::errorInfo());
                } else {
                    $this->Post->set('permitted', 'true');
                }
            }

            $this->detectStep();
        }

        public function validate()
        {
            $step = (!$this->Post->isNull('done') && $this->Get->get('step') === 'update')
                ? 'drop'
                : $this->Get->get('step');

            switch ($step) {
                case 'update':
                    /**
                     * check config.server.php condition
                     */
                    if (!Storage::exists(PATH_CONFIG)) {
                        $this->Error['config_exists'] = true;
                    }
                    if (!SetupCommon::checkPermission(PATH_CONFIG, '666')) {
                        $this->Error['config_permission'] = true;
                    }

                    /**
                     * check user privileges
                     */
                    if (!SetupCommon::checkAlterPerm()) {
                        $this->Error['alter_privilege'] = true;
                        $this->Post->set('mysqlErr', DB::errorCode() . ': ' . DB::errorInfo());
                    }
                    break;
                case 'drop':
                    break;
                default:
                    break;
            }
            $this->detectStep();
        }

        public function business()
        {
            $step = (!$this->Post->isNull('done') && $this->Get->get('step') === 'update')
                ? 'drop'
                : $this->Get->get('step');

            switch ($step) {
                case 'update':
                    $DB = DB::singleton(dsn());
                    @set_time_limit(0);

                    /**
                     * Schema Instance
                     */
                    $Schema = new Schema(dsn(), DB_NAME, DB_PREFIX);
                    $Schema->defSetYaml('schema');

                    /**
                     * Compare and Create Tables
                     */
                    $diff = $Schema->compareTables();
                    if (!empty($diff)) {
                        $Schema->createTables($diff, $Schema->defLoadYaml('index'));
                    }

                    /**
                     * Resolve Should Rename Columns
                     */
                    $history = $Schema->defLoadYaml('rename');
                    if (!empty($history)) {
                        $Schema->resolveRenames($history);
                    }

                    /**
                     * Compare and ADD-CHANGE Columns
                     */
                    $tbs = Schema::listUp($Schema->schema);
                    foreach ($tbs as $tb) {
                        $Schema->compareColumns($tb);
                        $Schema->resolveColumns($tb);
                    }

                    /**
                     * Create Index
                     */
                    $tbs = Schema::listUp($Schema->define);
                    $dbIndex = $Schema->getDatabaseIndexCurrent();
                    $newIndex = $Schema->defLoadYaml('index');
                    foreach ($tbs as $tb) {
                        if (in_array($tb, $diff, true)) {
                            continue;
                        }
                        $res = $Schema->compareIndex($tb, $dbIndex, $newIndex);
                        $Schema->makeIndex($tb, $res);
                    }

                    /**
                     * Rebuild config.server.php
                     */
                    SetupCommon::rebuildConfig();

                    /**
                     * Sepecific Update Process
                     */
                    $SQL = SQL::newSelect('sequence');
                    $SQL->addSelect('sequence_system_version');
                    $preVer = $DB->query($SQL->get(dsn()), 'one');
                    $this->versionUpdate($preVer);

                    /**
                     * Update Sequence System Version
                     */
                    $SQL = SQL::newUpdate('sequence');
                    $SQL->addUpdate('sequence_system_version', VERSION);
                    $DB->query($SQL->get(dsn()), 'exec');

                    break;

                case 'drop':
                    $DB = DB::singleton(dsn());

                    /**
                     * Drop Selected Columns
                     */
                    $Schema = new Schema(dsn(), DB_NAME, DB_PREFIX);

                    $tables = array_reduce(
                        $this->Post->getArray('column'),
                        function (array $tables, $value) {
                            list($table, $column) = explode('@', $value);

                            if (!array_key_exists($table, $tables)) {
                                return array_merge(
                                    $tables,
                                    [$table => [$column]]
                                );
                            }
                            return array_merge(
                                $tables,
                                [$table => array_merge(
                                    $tables[$table],
                                    [$column]
                                )
                                ]
                            );
                        },
                        []
                    );
                    foreach ($tables as $table => $columns) {
                        $Schema->dropColumns($table, $columns);
                    }
                    break;
                default:
                    break;
            }
            $this->detectStep();
        }

        public function detectStep()
        {
            $step = (!$this->Post->isNull('done') && $this->Get->get('step') === 'update')
                ? 'drop'
                : $this->Get->get('step');

            /**
             * detect from business method
             */
            if ($this->Post->get('step') === 'result') {
                switch ($step) {
                    case 'update':
                        $step = empty($this->Error) ? 'drop' : 'update:error';
                        break;
                    case 'drop':
                        $step = empty($this->Error) ? 'complete' : 'drop:error';
                        break;
                    default:
                        $step = empty($this->Error) ? 'update' : 'error';
                        break;
                }

                /**
                 * detect from validate method
                 */
            } else {
                switch ($step) {
                    case 'update':
                        $step = empty($this->Error) ? 'result' : 'update:error';
                        break;
                    case 'drop':
                        $step = empty($this->Error) ? 'result' : 'drop:error';
                        break;
                    default:
                        $step = empty($this->Error) ? 'result' : 'error';
                        break;
                }
            }

            Cache::flush('page');
            Cache::flush('template');
            Cache::flush('config');
            Cache::flush('field');
            Cache::flush('module');

            $this->Post->set('step', $step);
        }

        protected function versionUpdate($from_version)
        {
            $DB = DB::singleton(dsn());

            // v1.4.0以前
            if (version_compare($from_version, '1.4.0', '<')) {
                // コンフィグ画面用のconfigフィールドグループを追加

                // モジュールIDを探索
                $SQL = SQL::newSelect('module');
                $SQL->addWhereOpr('module_name', 'Links');
                $mods = $DB->query($SQL->get(dsn()), 'all');

                foreach ($mods as $mod) {
                    $mid = $mod['module_id'];
                    $bid = $mod['module_blog_id'];

                    $this->addGroupConfig('@linkgroup', ['links_value', 'links_label'], null, $mid, $bid);
                }

                // ルールを探索
                $SQL = SQL::newSelect('rule');
                $rules = $DB->query($SQL->get(dsn()), 'all');

                foreach ($rules as $rule) {
                    $rid = $rule['rule_id'];
                    $bid = $rule['rule_blog_id'];

                    $this->addGroupConfig('@linkgroup', ['links_value', 'links_label'], $rid, null, $bid);
                    $this->addGroupConfig(
                        '@addtype_group',
                        ['addtype_mimetype', 'addtype_extension'],
                        $rid,
                        null,
                        $bid
                    );
                    $this->addGroupConfig(
                        '@column_text_tag_group',
                        ['column_text_tag', 'column_text_tag_label'],
                        $rid,
                        null,
                        $bid
                    );
                    $this->addGroupConfig(
                        '@column_image_size_group',
                        ['column_image_size', 'column_image_size_label'],
                        $rid,
                        null,
                        $bid
                    );
                    $this->addGroupConfig(
                        '@column_map_size_group',
                        ['column_map_size', 'column_map_size_label'],
                        $rid,
                        null,
                        $bid
                    );
                    $this->addGroupConfig(
                        '@column_youtube_size_group',
                        ['column_youtube_size', 'column_youtube_size_label'],
                        $rid,
                        null,
                        $bid
                    );
                    $this->addGroupConfig(
                        '@column_eximage_size_group',
                        ['column_eximage_size', 'column_eximage_size_label'],
                        $rid,
                        null,
                        $bid
                    );
                    $this->addGroupConfig(
                        '@column_add_type_group',
                        ['column_add_type', 'column_add_type_label'],
                        $rid,
                        null,
                        $bid
                    );
                }
            }
            // v1.4.2以前
            if (version_compare($from_version, '1.4.2', '<')) {
                // Api_Yahoo_* の名前変更対応
                $SQL = SQL::newUpdate('module');
                $SQL->addUpdate('module_name', 'Api_Yahoo_WebSearch');
                $SQL->addWhereOpr('module_name', 'Api_YahooWebSearch');
                $DB->query($SQL->get(dsn()), 'exec');

                $SQL = SQL::newUpdate('module');
                $SQL->addUpdate('module_name', 'Api_Yahoo_ImageSearch');
                $SQL->addWhereOpr('module_name', 'Api_YahooImageSearch');
                $DB->query($SQL->get(dsn()), 'exec');

                // ユーザーfulltextを生成・追加
                $SQL = SQL::newSelect('user');
                $q = $SQL->get(dsn());
                $statement = $DB->query($q, 'exec');

                while ($row = $DB->next($statement)) {
                    // user
                    $user = [
                        $row['user_name'],
                        $row['user_code'],
                        $row['user_mail'],
                        $row['user_mail_mobile'],
                        $row['user_url']
                    ];
                    $uid = $row['user_id'];
                    $bid = $row['user_blog_id'];

                    // meta
                    $meta = [];
                    $SQL = SQL::newSelect('field');
                    $SQL->addSelect('field_value');
                    $SQL->addWhereOpr('field_search', 'on');
                    $SQL->addWhereOpr('field_uid', $uid);
                    $_q = $SQL->get(dsn());
                    $statement2 = $DB->query($_q, 'exec');

                    if ($statement2 && ($_row = $DB->next($statement2))) {
                        do {
                            $meta[] = $_row['field_value'];
                        } while ($_row = $DB->next($statement2));
                    }

                    // merge
                    $user = preg_replace('@\s+@', ' ', strip_tags(implode(' ', $user)));
                    $meta = preg_replace('@\s+@', ' ', strip_tags(implode(' ', $meta)));
                    $fulltext = $user . "\x0d\x0a\x0a\x0d" . $meta;

                    // delete
                    $SQL = SQL::newDelete('fulltext');
                    $SQL->addWhereOpr('fulltext_uid', $uid);
                    $DB->query($SQL->get(dsn()), 'exec');

                    // save
                    $SQL = SQL::newInsert('fulltext');
                    $SQL->addInsert('fulltext_value', $fulltext);
                    $SQL->addInsert('fulltext_uid', $uid);
                    $SQL->addInsert('fulltext_blog_id', $bid);
                    $DB->query($SQL->get(dsn()), 'exec');
                }
            }

            // v1.5.0以前
            if (version_compare($from_version, '1.5.0', '<')) {
                // navigation_publish = on を追加

                // モジュールIDを探索
                $SQL = SQL::newSelect('module');
                $SQL->addWhereOpr('module_name', 'Navigation');
                $mods = $DB->query($SQL->get(dsn()), 'all');

                if (!empty($mods)) {
                    foreach ($mods as $mod) {
                        $mid = $mod['module_id'];
                        $bid = $mod['module_blog_id'];

                        $this->addNavigationPublish(null, $mid, $bid);
                    }
                }

                // ルールを探索
                $SQL = SQL::newSelect('rule');
                $rules = $DB->query($SQL->get(dsn()), 'all');

                if (!empty($mods)) {
                    foreach ($rules as $rule) {
                        $rid = $rule['rule_id'];
                        $bid = $rule['rule_blog_id'];

                        $this->addNavigationPublish($rid, null, $bid);
                    }
                }

                // デフォルトを探索
                $SQL = SQL::newSelect('blog');
                $blogs = $DB->query($SQL->get(dsn()), 'all');

                if (!empty($blogs)) {
                    foreach ($blogs as $blog) {
                        $this->addNavigationPublish(null, null, $blog['blog_id']);
                    }
                }
            }
        }

        protected function addNavigationPublish($rid = null, $mid = null, $bid = null)
        {
            $DB = DB::singleton(dsn());
            $SQL = SQL::newSelect('config');
            $SQL->addWhereOpr('config_key', 'navigation_target');
            $SQL->addWhereOpr('config_rule_id', $rid);
            $SQL->addWhereOpr('config_module_id', $mid);
            $SQL->addWhereOpr('config_blog_id', $bid);
            $SQL->addSelect('*', 'row_amount', null, 'COUNT');
            $SQL->addSelect('config_sort', 'max_sort', null, 'MAX');
            $res = $DB->query($SQL->get(dsn()), 'row');

            if (empty($res)) {
                return false;
            }

            $range = range($res['max_sort'] + 1, $res['max_sort'] + $res['row_amount']);

            foreach ($range as $sort) {
                $SQL = SQL::newInsert('config');
                $SQL->addInsert('config_key', 'navigation_publish');
                $SQL->addInsert('config_value', 'on');
                $SQL->addInsert('config_sort', $sort);
                $SQL->addInsert('config_rule_id', $rid);
                $SQL->addInsert('config_module_id', $mid);
                $SQL->addInsert('config_blog_id', $bid);
                $DB->query($SQL->get(dsn()), 'exec');
            }
        }

        public function addGroupConfig($group, $vals, $rid = null, $mid = null, $bid = null)
        {
            $DB = DB::singleton(dsn());
            foreach ($vals as $val) {
                $SQL = SQL::newInsert('config');
                $SQL->addInsert('config_key', $group);
                $SQL->addInsert('config_value', $val);
                $SQL->addInsert('config_sort', '0');
                if (!empty($mid)) {
                    $SQL->addInsert('config_module_id', $mid);
                } elseif (!empty($rid)) {
                    $SQL->addInsert('config_rule_id', $rid);
                }
                $SQL->addInsert('config_blog_id', $bid);
                $DB->query($SQL->get(dsn()), 'exec');
            }
        }
    }

    new Surfacer_Update($tplPath);

    App::checkException();
} catch (Exception $e) {
    App::showError($e, false);
}
