<?php
/**
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 */
namespace Migrations;

use Cake\Core\Plugin as CorePlugin;
use Cake\Datasource\ConnectionManager;
use Migrations\Util\UtilTrait;
use Phinx\Config\Config;
use Symfony\Component\Console\Input\InputInterface;

/**
 * Contains a set of methods designed as overrides for
 * the methods in phinx that are responsible for reading the project configuration.
 * This is needed so that we can use the application configuration instead of having
 * a configuration yaml file.
 */
trait ConfigurationTrait
{
    use UtilTrait;

    /**
     * The configuration object that phinx uses for connecting to the database
     *
     * @var \Phinx\Config\Config
     */
    protected $configuration;

    /**
     * Connection name to be used for this request
     *
     * @var string
     */
    protected $connection;

    /**
     * The console input instance
     *
     * @var \Symfony\Component\Console\Input\Input
     */
    protected $input;

    /**
     * Overrides the original method from phinx in order to return a tailored
     * Config object containing the connection details for the database.
     *
     * @param bool $forceRefresh Refresh config.
     * @return \Phinx\Config\Config
     */
    public function getConfig($forceRefresh = false)
    {
        if ($this->configuration && $forceRefresh === false) {
            return $this->configuration;
        }

        $migrationsPath = $this->getOperationsPath($this->input);
        $seedsPath = $this->getOperationsPath($this->input, 'Seeds');
        $plugin = $this->getPlugin($this->input);

        if (!is_dir($migrationsPath)) {
            mkdir($migrationsPath, 0777, true);
        }

        if (!is_dir($seedsPath)) {
            mkdir($seedsPath, 0777, true);
        }

        $phinxTable = $this->getPhinxTable($plugin);

        $connection = $this->getConnectionName($this->input);

        $connectionConfig = ConnectionManager::getConfig($connection);
        $adapterName = $this->getAdapterName($connectionConfig['driver']);

        $templatePath = __DIR__ . DS . 'Template' . DS;
        $config = [
            'paths' => [
                'migrations' => $migrationsPath,
                'seeds' => $seedsPath,
            ],
            'templates' => [
                'file' => $templatePath . 'Phinx' . DS . 'create.php.template',
            ],
            'migration_base_class' => 'Migrations\AbstractMigration',
            'environments' => [
                'default_migration_table' => $phinxTable,
                'default_database' => 'default',
                'default' => [
                    'adapter' => $adapterName,
                    'host' => isset($connectionConfig['host']) ? $connectionConfig['host'] : null,
                    'user' => isset($connectionConfig['username']) ? $connectionConfig['username'] : null,
                    'pass' => isset($connectionConfig['password']) ? $connectionConfig['password'] : null,
                    'port' => isset($connectionConfig['port']) ? $connectionConfig['port'] : null,
                    'name' => $connectionConfig['database'],
                    'charset' => isset($connectionConfig['encoding']) ? $connectionConfig['encoding'] : null,
                    'unix_socket' => isset($connectionConfig['unix_socket']) ? $connectionConfig['unix_socket'] : null,
                    'suffix' => '',
                ],
            ],
        ];

        if ($adapterName === 'pgsql') {
            if (!empty($connectionConfig['schema'])) {
                $config['environments']['default']['schema'] = $connectionConfig['schema'];
            }
        }

        if ($adapterName === 'mysql') {
            if (!empty($connectionConfig['ssl_key']) && !empty($connectionConfig['ssl_cert'])) {
                $config['environments']['default']['mysql_attr_ssl_key'] = $connectionConfig['ssl_key'];
                $config['environments']['default']['mysql_attr_ssl_cert'] = $connectionConfig['ssl_cert'];
            }

            if (!empty($connectionConfig['ssl_ca'])) {
                $config['environments']['default']['mysql_attr_ssl_ca'] = $connectionConfig['ssl_ca'];
            }
        }

        return $this->configuration = new Config($config);
    }

    /**
     * Returns the correct driver name to use in phinx based on the driver class
     * that was configured for the configuration.
     *
     * @param string $driver The driver name as configured for the CakePHP app.
     * @return string Name of the adapter.
     * @throws \InvalidArgumentException when it was not possible to infer the information
     * out of the provided database configuration
     */
    public function getAdapterName($driver)
    {
        switch ($driver) {
            case 'Cake\Database\Driver\Mysql':
            case is_subclass_of($driver, 'Cake\Database\Driver\Mysql'):
                return 'mysql';
            case 'Cake\Database\Driver\Postgres':
            case is_subclass_of($driver, 'Cake\Database\Driver\Postgres'):
                return 'pgsql';
            case 'Cake\Database\Driver\Sqlite':
            case is_subclass_of($driver, 'Cake\Database\Driver\Sqlite'):
                return 'sqlite';
            case 'Cake\Database\Driver\Sqlserver':
            case is_subclass_of($driver, 'Cake\Database\Driver\Sqlserver'):
                return 'sqlsrv';
        }

        throw new \InvalidArgumentException('Could not infer database type from driver');
    }

    /**
     * Returns the connection name that should be used for the migrations.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input the input object
     * @return string
     */
    protected function getConnectionName(InputInterface $input)
    {
        $connection = 'default';
        if ($input->getOption('connection')) {
            $connection = $input->getOption('connection');
        }

        return $connection;
    }
}
