<?php

/**
 * BoxBilling.
 *
 * @copyright BoxBilling, Inc (https://www.boxbilling.org)
 * @license   Apache-2.0
 *
 * Copyright BoxBilling, Inc
 * This source file is subject to the Apache-2.0 License that is bundled
 * with this source code in the file LICENSE
 */

declare(strict_types=1);

$di = new Box_Di();

$di['config'] = function () {
    $array = include BB_PATH_ROOT.'/bb-config.php';

    return new Box_Config($array);
};

$di['logger'] = function () use ($di) {
    $log = new Box_Log();
    $log->setDi($di);

    $log_to_db = isset($di['config']['log_to_db']) && $di['config']['log_to_db'];

    if ($log_to_db) {
        $activity_service = $di['mod_service']('activity');
        $writer2 = new Box_LogDb($activity_service);

        if ($di['auth']->isAdminLoggedIn()) {
            $admin = $di['loggedin_admin'];
            $log->setEventItem('admin_id', $admin->id);
        } elseif ($di['auth']->isClientLoggedIn()) {
            $client = $di['loggedin_client'];
            $log->setEventItem('client_id', $client->id);
        }

        $log->addWriter($writer2);
    } else {
        $logFile = $di['config']['path_logs'];
        $writer = new Box_LogStream($logFile);
        $log->addWriter($writer);
    }

    return $log;
};

$di['crypt'] = function () use ($di) {
    $crypt = new Box_Crypt();
    $crypt->setDi($di);

    return $crypt;
};

$di['pdo'] = function () use ($di) {
    $c = $di['config']['db'];

    $pdo = new PDO($c['type'].':host='.$c['host'].';port='.$c['port'].';dbname='.$c['name'],
        $c['user'],
        $c['password'],
        [
            PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]
    );

    if (isset($c['debug']) && $c['debug']) {
        $pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['Box_DbLoggedPDOStatement']);
    }

    if ('mysql' == $c['type']) {
        $pdo->exec('SET NAMES "utf8"');
        $pdo->exec('SET CHARACTER SET utf8');
        $pdo->exec('SET CHARACTER_SET_CONNECTION = utf8');
        $pdo->exec('SET character_set_results = utf8');
        $pdo->exec('SET character_set_server = utf8');
        $pdo->exec('SET SESSION interactive_timeout = 28800');
        $pdo->exec('SET SESSION wait_timeout = 28800');
    }

    return $pdo;
};

$di['db'] = function () use ($di) {
    require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'rb.php';
    R::setup($di['pdo']);
    \RedBeanPHP\Util\DispenseHelper::setEnforceNamingPolicy(false);

    $helper = new Box_BeanHelper();
    $helper->setDi($di);

    $mapper = new \RedBeanPHP\Facade();
    $mapper->getRedBean()->setBeanHelper($helper);
    $freeze = isset($di['config']['db']['freeze']) ? (bool) $di['config']['db']['freeze'] : true;
    $mapper->freeze($freeze);

    $db = new Box_Database();
    $db->setDi($di);
    $db->setDataMapper($mapper);

    return $db;
};

$di['pager'] = function () use ($di) {
    $service = new Box_Pagination();
    $service->setDi($di);

    return $service;
};

$di['url'] = function () use ($di) {
    $url = new Box_Url();
    $url->setDi($di);
    $url->setBaseUri(BB_URL);

    return $url;
};

$di['mod'] = $di->protect(function ($name) use ($di) {
    $mod = new Box_Mod($name);
    $mod->setDi($di);

    return $mod;
});

$di['mod_service'] = $di->protect(function ($mod, $sub = '') use ($di) {
    return $di['mod']($mod)->getService($sub);
});

$di['mod_config'] = $di->protect(function ($name) use ($di) {
    return $di['mod']($name)->getConfig();
});

$di['events_manager'] = function () use ($di) {
    $service = new Box_EventManager();
    $service->setDi($di);

    return $service;
};

$di['session'] = function () use ($di) {
    $handler = new PdoSessionHandler($di['pdo']);

    return new Box_Session($handler);
};

$di['cookie'] = function () use ($di) {
    $service = new Box_Cookie();
    $service->setDi($di);

    return $service;
};

$di['request'] = function () use ($di) {
    $service = new Box_Request();
    $service->setDi($di);

    return $service;
};

$di['cache'] = function () { return new FileCache(); };

$di['auth'] = function () use ($di) { return new Box_Authorization($di); };

$di['twig'] = $di->factory(function () use ($di) {
    $config = $di['config'];
    $options = $config['twig'];

    $loader = new Twig\Loader\ArrayLoader();
    $twig = new Twig\Environment($loader, $options);

    $box_extensions = new Box_TwigExtensions();
    $box_extensions->setDi($di);

    // $twig->addExtension(new Twig\Extension\OptimizerExtension());
    $twig->addExtension(new \Twig\Extension\StringLoaderExtension());
    $twig->addExtension(new Twig\Extension\DebugExtension());
    $twig->addExtension(new Symfony\Bridge\Twig\Extension\TranslationExtension());
    $twig->addExtension($box_extensions);
    $twig->getExtension(Twig\Extension\CoreExtension::class)
        ->setDateFormat($config['locale_date_format']);
    $twig->getExtension(Twig\Extension\CoreExtension::class)
        ->setTimezone($config['timezone']);

    // add globals
    if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'XMLHttpRequest' == $_SERVER['HTTP_X_REQUESTED_WITH']) {
        $_GET['ajax'] = true;
    }

    $twig->addGlobal('request', $_GET);
    $twig->addGlobal('guest', $di['api_guest']);

    return $twig;
});

$di['is_client_logged'] = function () use ($di) {
    if (!$di['auth']->isClientLoggedIn()) {
        $api_str = '/api/';
        $url = $di['request']->getQuery('_url');

        if (0 === strncasecmp($url, $api_str, strlen($api_str))) {
            // Throw Exception if api request
            throw new Exception('Client is not logged in');
        } else {
            // Redirect to login page if browser request
            $login_url = $di['url']->link('login');
            header("Location: $login_url");
        }
    }

    return true;
};

$di['is_admin_logged'] = function () use ($di) {
    if (!$di['auth']->isAdminLoggedIn()) {
        $api_str = '/api/';
        $url = $di['request']->getQuery('_url');
        if (0 === strncasecmp($url, $api_str, strlen($api_str))) {
            // Throw Exception if api request
            throw new Exception('Admin is not logged in');
        } else {
            // Redirect to login page if browser request
            $login_url = $di['url']->adminLink('staff/login');
            header("Location: $login_url");
        }
    }

    return true;
};

$di['loggedin_client'] = function () use ($di) {
    $di['is_client_logged'];
    $client_id = $di['session']->get('client_id');

    return $di['db']->getExistingModelById('Client', $client_id);
};

$di['loggedin_admin'] = function () use ($di) {
    if ('cli' == php_sapi_name() || 'cgi' == substr(php_sapi_name(), 0, 3)) {
        return $di['mod_service']('staff')->getCronAdmin();
    }

    $di['is_admin_logged'];
    $admin = $di['session']->get('admin');

    return $di['db']->getExistingModelById('Admin', $admin['id']);
};

$di['api'] = $di->protect(function ($role) use ($di) {
    switch ($role) {
        case 'guest':
            $identity = new \Model_Guest();
            break;

        case 'client':
            $identity = $di['loggedin_client'];
            break;

        case 'admin':
            $identity = $di['loggedin_admin'];
            break;

        case 'system':
            $identity = $di['mod_service']('staff')->getCronAdmin();
           break;

        default:
            throw new Exception('Unrecognized Handler type: '.$role);
    }

    $api = new Api_Handler($identity);
    $api->setDi($di);

    return $api;
});

$di['api_guest'] = function () use ($di) { return $di['api']('guest'); };
$di['api_client'] = function () use ($di) { return $di['api']('client'); };
$di['api_admin'] = function () use ($di) { return $di['api']('admin'); };
$di['api_system'] = function () use ($di) { return $di['api']('system'); };

$di['tools'] = function () use ($di) {
    $service = new Box_Tools();
    $service->setDi($di);

    return $service;
};

$di['validator'] = function () use ($di) {
    $validator = new Box_Validate();
    $validator->setDi($di);

    return $validator;
};

$di['guzzle_client'] = function () use ($di) {
    return new GuzzleHttp\Client([
        'headers' => [
            'User-Agent' => $di['config']['guzzle']['user_agent'],
            'Upgrade-Insecure-Requests' => $di['config']['guzzle']['upgrade_insecure_requests'],
        ],
        'timeout' => $di['config']['guzzle']['timeout'],
    ]);
};

$di['mail'] = function () {
    return new Box_Mail();
};

$di['extension'] = function () use ($di) {
    $extension = new \Box_Extension();
    $extension->setDi($di);

    return $extension;
};

$di['updater'] = function () use ($di) {
    $updater = new \Box_Update();
    $updater->setDi($di);

    return $updater;
};

$di['curl'] = function ($url) use ($di) {
    $curl = new \Box_Curl($url);
    $curl->setDi($di);

    return $curl;
};

$di['zip_archive'] = function () {return new ZipArchive(); };

$di['server_package'] = function () {return new Server_Package(); };
$di['server_client'] = function () {return new Server_Client(); };
$di['server_account'] = function () {return new Server_Account(); };

$di['server_manager'] = $di->protect(function ($manager, $config) {
    $class = sprintf('Server_Manager_%s', ucfirst($manager));

    return new $class($config);
});

$di['requirements'] = function () use ($di) {
    $r = new Box_Requirements();
    $r->setDi($di);

    return $r;
};

$di['period'] = $di->protect(function ($code) { return new \Box_Period($code); });

$di['theme'] = function () use ($di) {
    $service = $di['mod_service']('theme');

    return $service->getCurrentClientAreaTheme();
};
$di['cart'] = function () use ($di) {
    $service = $di['mod_service']('cart');

    return $service->getSessionCart();
};

$di['table'] = $di->protect(function ($name) use ($di) {
    $tools = new Box_Tools();
    $tools->setDi($di);
    $table = $tools->getTable($name);
    $table->setDi($di);

    return $table;
});

$di['license_server'] = function () use ($di) {
    $server = new \Box\Mod\Servicelicense\Server($di['logger']);
    $server->setDi($di);

    return $server;
};

$di['solusvm'] = $di->protect(function ($config) use ($di) {
    $solusVM = new \Box\Mod\Servicesolusvm\SolusVM();
    $solusVM->setDi($di);
    $solusVM->setConfig($config);

    return $solusVM;
});

$di['service_boxbilling'] = $di->protect(function ($config) {
    $service = new \Box\Mod\Serviceboxbillinglicense\ServiceBoxbilling($config);

    return $service;
});

$di['ftp'] = $di->protect(function ($params) { return new \Box_Ftp($params); });

$di['pdf'] = function () use ($di) {
    include BB_PATH_LIBRARY.'/PDF_ImageAlpha.php';

    return new \PDF_ImageAlpha();
};

$di['geoip'] = function () { return new \GeoIp2\Database\Reader(BB_PATH_LIBRARY.'/GeoLite2-Country.mmdb'); };

$di['password'] = function () { return new Box_Password(); };

$di['translate'] = $di->protect(function ($textDomain = '') use ($di) {
    $tr = new Box_Translate();
    
    if (!empty($textDomain)) {
        $tr->setDomain($textDomain);
    }

    $cookieBBlang = $di['cookie']->get('BBLANG');
    $locale = !empty($cookieBBlang) ? $cookieBBlang : $di['config']['locale'];

    $tr->setDi($di);
    $tr->setLocale($locale);
    $tr->setup();

    return $tr;
});

$di['array_get'] = $di->protect(function (array $array, $key, $default = null) {
    return array_key_exists($key, $array) ? $array[$key] : $default;
});

return $di;
