<?php

namespace Mtc\Core\Admin;

use Illuminate\Database\Schema\MySqlBuilder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use MTC\Core\Setting;

/**
 * Module installer base class
 * @author Aleksey Lavrinenko <aleksey.lavrinenko@mtcmedia.co.uk>
 * Created on 22.07.2017.
 */
abstract class ModuleInstaller
{
    /**
     * Module name. To be overridden in the child class
     *
     * @var string
     */
    protected $module_name;

    /**
     * Constant for menu entries.
     *
     * @var string
     */
    protected $menu_constant;

    /**
     * Illuminate schema builder
     *
     * @var MySqlBuilder
     */
    protected $schema;

    /**
     * Runs DB modifications to install the module.
     */
    abstract protected function up();

    /**
     * Runs DB modifications to uninstall the module. The opposite of `up()`
     */
    abstract protected function down();

    /**
     * Returns settings for insertion into the DB.
     *
     * @return array
     */
    abstract protected function getSettings();

    /**
     * Inserts admin menu entries
     */
    abstract protected function insertAdminMenu();

    /**
     * ModuleInstaller constructor.
     */
    public function __construct()
    {
        $this->schema = Schema::getFacadeRoot();
        $this->validate();
    }

    /**
     * Installs the module
     */
    public function install()
    {
        $this->insertSettings();
        $this->insertAdminMenu();
        $this->up();
    }

    /**
     * Uninstalls the module
     */
    public function uninstall()
    {
        $this->down();
        $this->deleteAdminMenu();
        $this->deleteSettings();
    }

    /**
     * Validates the module class
     */
    protected function validate()
    {
        if (!$this->module_name) {
            throw new \RuntimeException('Module name not defined in $module_name');
        }

        if (!$this->menu_constant) {
            throw new \RuntimeException('Menu constant not defined in $menu_constant');
        }
    }

    /**
     * Inserts settings into DB and removes settings files to flush the cache.
     */
    protected function insertSettings()
    {
        $settings = $this->getSettings();

        if (empty($settings)) {
            return;
        }

        $settings = array_map(
            function ($setting_array) {
                $setting_array['module'] = $this->module_name;
                return $setting_array;
            },
            $settings
        );
        Setting::insert($settings);
        $this->clearSettingsCache();
    }

    /**
     * Deletes settings from DB and removes settings cache files to flush the cache.
     */
    protected function deleteSettings()
    {
        $keys = array_column($this->getSettings(), 'key');
        Setting::whereIn('key', $keys)->delete();
        $this->clearSettingsCache();
    }

    /**
     * Deletes module admin menu entries
     */
    protected function deleteAdminMenu()
    {
        DB::table('admin_menu')
            ->where('constant', '=', $this->menu_constant)
            ->delete();
    }

    /**
     * Remove settings cache files
     */
    protected function clearSettingsCache()
    {
        unlink(Setting::getFilePath());
        unlink(Setting::getFilePath() . '.cache');
    }
}
