<?php
/**
 * CpanelProvisioner
 * Talks to cPanel's UAPI to create a brand-new MySQL database + database
 * user for a new company, and grant that user full privileges on it.
 *
 * Your regular app database user CANNOT create new databases (shared
 * hosting restricts this on purpose). cPanel's own API can, because it
 * runs with your hosting account's full privileges.
 *
 * SETUP REQUIRED (one-time, in cPanel):
 *   1. cPanel -> Security -> Manage API Tokens -> create a token.
 *   2. Fill in the constants below.
 */
define('CPANEL_HOSTNAME', 'cpanel.adrieltech.com');            // cPanel access hostname
define('CPANEL_USERNAME', 'adrieltech');                       // your cPanel account username
define('CPANEL_API_TOKEN', '3FRBWPYO1081DFQKGR8QI91E9F3J3R3D');       // cPanel > Security > Manage API Tokens > Create
define('CPANEL_PORT', 2083);

class CpanelProvisioner
{
    private string $base;
    private string $auth;

    public function __construct()
    {
        $this->base = "https://" . CPANEL_HOSTNAME . ":" . CPANEL_PORT . "/execute";
        $this->auth = "cpanel " . CPANEL_USERNAME . ":" . CPANEL_API_TOKEN;
    }

    private function call(string $module, string $function, array $params = []): array
    {
        $url = $this->base . "/{$module}/{$function}?" . http_build_query($params);
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => ["Authorization: " . $this->auth],
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_TIMEOUT => 30,
        ]);
        $response = curl_exec($ch);
        if ($response === false) {
            throw new Exception('cPanel API request failed: ' . curl_error($ch));
        }
        curl_close($ch);
        $data = json_decode($response, true);
        if (!$data || (isset($data['status']) && $data['status'] != 1)) {
            $msg = $data['errors'][0] ?? ($data['error'] ?? 'Unknown cPanel API error');
            throw new Exception("cPanel API error ({$module}/{$function}): {$msg}");
        }
        return $data;
    }

    /**
     * Create a new database, a new database user, and grant that user
     * full privileges on the new database.
     *
     * @param string $dbNameSuffix  short slug for the company e.g. "abccorp"
     * @param string $dbPassword    plain-text password to assign to the new db user
     * @return array{db_name:string, db_user:string}
     */
    public function provisionDatabase(string $dbNameSuffix, string $dbPassword): array
    {
        // cPanel on this server does NOT auto-prefix - we must pass the full
        // "cpanelusername_something" name ourselves in every API call.
        $dbNameSuffix = preg_replace('/[^a-z0-9_]/', '', strtolower($dbNameSuffix));
        $dbName = CPANEL_USERNAME . '_' . substr($dbNameSuffix, 0, 32 - strlen(CPANEL_USERNAME) - 1);
        $dbUser = CPANEL_USERNAME . '_' . substr($dbNameSuffix, 0, 16 - strlen(CPANEL_USERNAME) - 1);

        $this->call('Mysql', 'create_database', ['name' => $dbName]);
        $this->call('Mysql', 'create_user', ['name' => $dbUser, 'password' => $dbPassword]);
        $this->call('Mysql', 'set_privileges_on_database', [
            'user' => $dbUser,
            'database' => $dbName,
            'privileges' => 'ALL PRIVILEGES',
        ]);

        return [
            'db_name' => $dbName,
            'db_user' => $dbUser,
        ];
    }

    /**
     * Undo a provisionDatabase() call - used when something fails partway
     * through creating a new company, so a retry doesn't hit
     * "database already exists" for a half-finished attempt.
     * Safe to call even if the db/user don't exist (ignores that error).
     */
    public function dropDatabase(string $dbNameSuffix): void
    {
        $dbNameSuffix = preg_replace('/[^a-z0-9_]/', '', strtolower($dbNameSuffix));
        $dbName = CPANEL_USERNAME . '_' . substr($dbNameSuffix, 0, 32 - strlen(CPANEL_USERNAME) - 1);
        $dbUser = CPANEL_USERNAME . '_' . substr($dbNameSuffix, 0, 16 - strlen(CPANEL_USERNAME) - 1);
        try { $this->call('Mysql', 'delete_database', ['name' => $dbName]); } catch (Exception $e) {}
        try { $this->call('Mysql', 'delete_user', ['name' => $dbUser]); } catch (Exception $e) {}
    }
}