<?php
/*
 * Fusio
 * A web-application to create dynamically RESTful APIs
 *
 * Copyright (C) 2015-2022 Christoph Kappestein <christoph.kappestein@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

namespace Fusio\Impl\Provider;

use Doctrine\DBAL\Connection;
use Fusio\Adapter;

/**
 * ProviderLoader
 *
 * @author  Christoph Kappestein <christoph.kappestein@gmail.com>
 * @license http://www.gnu.org/licenses/agpl-3.0
 * @link    https://www.fusio-project.org
 */
class ProviderLoader
{
    private Connection $connection;
    private string $file;
    private ?ProviderConfig $config = null;

    public function __construct(Connection $connection, string $file)
    {
        $this->connection = $connection;
        $this->file       = $file;
    }

    public function getConfig(): ProviderConfig
    {
        if ($this->config) {
            return $this->config;
        }

        $config = $this->getDefaultConfig();

        // read from database
        $result = $this->connection->fetchAll('SELECT type, class FROM fusio_provider ORDER BY class ASC');
        foreach ($result as $row) {
            $this->mergeClass($config, $row['type'], $row['class']);
        }

        // read from file
        if (!empty($this->file)) {
            $result = include($this->file);

            if (is_array($result)) {
                foreach ($result as $type => $classes) {
                    foreach ($classes as $class) {
                        $this->mergeClass($config, $type, $class);
                    }
                }
            }
        }

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

    public function reset(): void
    {
        $this->config = null;
    }

    private function mergeClass(array &$config, $type, $class): void
    {
        if (isset($config[$type])) {
            if (class_exists($class) && !in_array($class, $config[$type])) {
                $config[$type][] = $class;
            }
        }
    }

    private function getDefaultConfig(): array
    {
        return [
            ProviderConfig::TYPE_ACTION => [
                Adapter\Cli\Action\CliProcessor::class,
                Adapter\Fcgi\Action\FcgiProcessor::class,
                Adapter\File\Action\FileProcessor::class,
                Adapter\GraphQL\Action\GraphQLProcessor::class,
                Adapter\Http\Action\HttpProcessor::class,
                Adapter\Php\Action\PhpProcessor::class,
                Adapter\Php\Action\PhpSandbox::class,
                Adapter\Smtp\Action\SmtpSend::class,
                Adapter\Sql\Action\SqlSelectAll::class,
                Adapter\Sql\Action\SqlSelectRow::class,
                Adapter\Sql\Action\SqlInsert::class,
                Adapter\Sql\Action\SqlUpdate::class,
                Adapter\Sql\Action\SqlDelete::class,
                Adapter\Sql\Action\Query\SqlQueryAll::class,
                Adapter\Sql\Action\Query\SqlQueryRow::class,
                Adapter\Util\Action\UtilStaticResponse::class,
            ],
            ProviderConfig::TYPE_CONNECTION => [
                Adapter\Http\Connection\Http::class,
                Adapter\Sql\Connection\Sql::class,
                Adapter\Sql\Connection\SqlAdvanced::class,
            ],
            ProviderConfig::TYPE_PAYMENT => [
            ],
            ProviderConfig::TYPE_USER => [
                User\Facebook::class,
                User\Github::class,
                User\Google::class,
            ],
            ProviderConfig::TYPE_GENERATOR => [
                Adapter\Sql\Generator\SqlTable::class,
                Adapter\Sql\Generator\SqlEntity::class,
            ],
        ];
    }
}
