<?php

use Acms\Services\Update\Engine;
use Acms\Services\Facades\Application;

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':
                    /** @var \Acms\Services\Update\Logger $logger */
                    $logger = Application::make('update.logger');

                    $updateService = new Engine($logger);

                    $updateService->validate(true);
                    $updateService->dbUpdate();

                    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);
        }
    }

    new Surfacer_Update($tplPath);

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