<?php

namespace App\Modules\Product\CentosWebPanel;

use App\Core\Product\AbstractProduct;
use App\Core\Product\Events\AccountCreated;

class CentosWebPanel extends AbstractProduct
{
    /**
     * Default CWP API port
     */
    protected $apiPort = 2304;

    /**
     * CWP module requires a server
     */
    public function requiresServer(): bool
    {
        return true;
    }

    /**
     * Create a new hosting account on CWP
     *
     * CWP API: POST /account with action=add
     *
     * @param  array  $params  Account parameters
     * @return array Operation result
     */
    public function createAccount(array $params)
    {
        $requiredParams = ['username', 'password', 'domain', 'email'];

        foreach ($requiredParams as $param) {
            if (! isset($params[$param]) || empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.cwp_missing_param', ['param' => $param]),
                ];
            }
        }

        $package = $params['plan']
            ?? $params['package']
            ?? $this->moduleConfig($params, 'default_package', 'default');

        $server = $this->serverConfig($params);
        $serverIps = $params['server_ips']
            ?? $this->moduleConfig($params, 'server_ip', $server['ip'] ?? '');

        $postData = [
            'action' => 'add',
            'domain' => $params['domain'],
            'user' => $params['username'],
            'pass' => $params['password'],
            'email' => $params['email'],
            'package' => $package,
            'inode' => $params['inode'] ?? '0',
            'limit_nproc' => $params['limit_nproc'] ?? '40',
            'limit_nofile' => $params['limit_nofile'] ?? '150',
            'server_ips' => $serverIps,
        ];

        $response = $this->callApi('account', $postData, $params);

        if ($response['success']) {
            $result = [
                'status' => 'success',
                'message' => __('admin/modules.cwp_account_created_success'),
                'data' => $response['data'],
            ];

            AccountCreated::dispatch($params, $result);

            return $result;
        }

        $result = [
            'status' => 'error',
            'message' => $response['message'] ?? __('admin/modules.cwp_account_create_failed'),
            'data' => $response['data'],
        ];

        AccountCreated::dispatch($params, $result);

        return $result;
    }

    /**
     * Suspend a hosting account on CWP
     *
     * CWP API: POST /account with action=susp
     *
     * @param  array  $params  Account parameters
     * @return array Operation result
     */
    public function suspendAccount(array $params)
    {
        if (! isset($params['username']) || empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        $postData = [
            'action' => 'susp',
            'user' => $params['username'],
        ];

        $response = $this->callApi('account', $postData, $params);

        if ($response['success']) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.cwp_account_suspended_success'),
            ];
        }

        return [
            'status' => 'error',
            'message' => $response['message'] ?? __('admin/modules.cwp_account_suspend_failed'),
            'data' => $response['data'],
        ];
    }

    /**
     * Unsuspend a hosting account on CWP
     *
     * CWP API: POST /account with action=unsp
     *
     * @param  array  $params  Account parameters
     * @return array Operation result
     */
    public function unsuspendAccount(array $params)
    {
        if (! isset($params['username']) || empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        $postData = [
            'action' => 'unsp',
            'user' => $params['username'],
        ];

        $response = $this->callApi('account', $postData, $params);

        if ($response['success']) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.cwp_account_unsuspended_success'),
            ];
        }

        return [
            'status' => 'error',
            'message' => $response['message'] ?? __('admin/modules.cwp_account_unsuspend_failed'),
            'data' => $response['data'],
        ];
    }

    /**
     * Terminate (delete) a hosting account on CWP
     *
     * CWP API: POST /account with action=del
     *
     * @param  array  $params  Account parameters
     * @return array Operation result
     */
    public function terminateAccount(array $params)
    {
        if (! isset($params['username']) || empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        $postData = [
            'action' => 'del',
            'user' => $params['username'],
            'email' => $params['email'] ?? '',
        ];

        $response = $this->callApi('account', $postData, $params);

        if ($response['success']) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.cwp_account_deleted_success'),
            ];
        }

        return [
            'status' => 'error',
            'message' => $response['message'] ?? __('admin/modules.cwp_account_delete_failed'),
            'data' => $response['data'],
        ];
    }

    /**
     * Change the password for a hosting account on CWP
     *
     * CWP API: POST /changepass with action=udp (api.php:128-130)
     *
     * @param  array  $params  Account parameters
     * @return array Operation result
     */
    public function changeAccountPassword(array $params)
    {
        if (! isset($params['username']) || empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        if (! isset($params['password']) || empty($params['password'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_password_required'),
            ];
        }

        $postData = [
            'action' => 'udp',
            'user' => $params['username'],
            'pass' => $params['password'],
        ];

        $response = $this->callApi('changepass', $postData, $params);

        if ($response['success']) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.cwp_password_changed_success'),
            ];
        }

        return [
            'status' => 'error',
            'message' => $response['message'] ?? __('admin/modules.cwp_password_change_failed'),
            'data' => $response['data'],
        ];
    }

    /**
     * Change the hosting package for an account on CWP
     *
     * CWP API: POST /changepack with action=udp (api.php:131-133)
     *
     * @param  array  $params  Account parameters
     * @return array Operation result
     */
    public function changeAccountPackage(array $params)
    {
        if (! isset($params['username']) || empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        $package = $params['plan'] ?? $params['package'] ?? null;

        if (empty($package)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_package_required'),
            ];
        }

        $postData = [
            'action' => 'udp',
            'user' => $params['username'],
            'package' => $package,
        ];

        $response = $this->callApi('changepack', $postData, $params);

        if ($response['success']) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.cwp_package_changed_success'),
            ];
        }

        return [
            'status' => 'error',
            'message' => $response['message'] ?? __('admin/modules.cwp_package_change_failed'),
            'data' => $response['data'],
        ];
    }

    /**
     * Test the connection to the CWP server
     *
     * CWP API: POST /account with action=list to verify connectivity
     *
     * @param  array  $params  Test parameters
     * @return array Test result
     */
    public function testConnection(array $params = [])
    {
        $postData = [
            'action' => 'list',
        ];

        $response = $this->callApi('account', $postData, $params);

        if ($response['success']) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.cwp_connection_test_success'),
                'data' => $response['data'],
            ];
        }

        return [
            'status' => 'error',
            'message' => __('admin/modules.cwp_connection_test_failed'),
            'data' => $response['data'],
        ];
    }

    /**
     * Return admin panel configuration fields
     *
     * @return array Admin field definitions
     */
    public function adminFields()
    {
        return [
            'hostname' => [
                'name' => __('admin/modules.cwp_field_hostname'),
                'type' => 'text',
                'description' => __('admin/modules.cwp_field_hostname_description'),
                'required' => true,
            ],
            'port' => [
                'name' => __('admin/modules.cwp_field_port'),
                'type' => 'text',
                'description' => __('admin/modules.cwp_field_port_description'),
                'default' => '2304',
                'required' => false,
            ],
            'api_key' => [
                'name' => __('admin/modules.cwp_field_api_key'),
                'type' => 'password',
                'description' => __('admin/modules.cwp_field_api_key_description'),
                'required' => true,
            ],
            'use_ssl' => [
                'name' => __('admin/modules.cwp_field_use_ssl'),
                'type' => 'checkbox',
                'description' => __('admin/modules.cwp_field_use_ssl_description'),
                'default' => true,
            ],
        ];
    }

    /**
     * Return client-facing fields (shown during order/service)
     *
     * @return array Client field definitions
     */
    public function clientFields()
    {
        return [
            'domain' => [
                'name' => __('admin/modules.cwp_field_domain'),
                'type' => 'text',
                'description' => __('admin/modules.cwp_field_domain_description'),
                'required' => true,
            ],
            'username' => [
                'name' => __('admin/modules.cwp_field_client_username'),
                'type' => 'text',
                'description' => __('admin/modules.cwp_field_client_username_description'),
                'required' => true,
            ],
            'password' => [
                'name' => __('admin/modules.cwp_field_client_password'),
                'type' => 'password',
                'description' => __('admin/modules.cwp_field_client_password_description'),
                'required' => true,
            ],
        ];
    }

    /**
     * Return server management fields
     *
     * @return array Server field definitions
     */
    public function serverFields()
    {
        return [
            'api_key' => [
                'name' => __('admin/modules.cwp_field_api_key'),
                'type' => 'password',
                'description' => __('admin/modules.cwp_field_api_key_description'),
                'required' => true,
            ],
        ];
    }

    /**
     * Return admin panel custom buttons
     *
     * @return array Admin button definitions
     */
    public function getAdminButtons(): array
    {
        return [
            [
                'action' => 'list_packages',
                'label' => __('admin/modules.cwp_btn_list_packages'),
                'icon' => 'fas fa-box',
                'class' => 'btn-outline-info',
            ],
            [
                'action' => 'account_details',
                'label' => __('admin/modules.cwp_btn_account_details'),
                'icon' => 'fas fa-info-circle',
                'class' => 'btn-outline-primary',
            ],
            [
                'action' => 'check_status',
                'label' => __('admin/modules.cwp_btn_check_status'),
                'icon' => 'fas fa-check-circle',
                'class' => 'btn-outline-success',
            ],
            [
                'action' => 'list_emails',
                'label' => __('admin/modules.cwp_btn_list_emails'),
                'icon' => 'fas fa-envelope',
                'class' => 'btn-outline-secondary',
            ],
            [
                'action' => 'list_domains',
                'label' => __('admin/modules.cwp_btn_list_domains'),
                'icon' => 'fas fa-globe',
                'class' => 'btn-outline-secondary',
            ],
        ];
    }

    /**
     * Handle admin custom actions
     *
     * @param  string  $action  Action name
     * @param  array  $params  Parameters
     * @return array Operation result
     */
    public function adminCustomAction(string $action, array $params): array
    {
        switch ($action) {
            case 'list_packages':
                return $this->adminListPackages($params);

            case 'account_details':
                return $this->adminAccountDetails($params);

            case 'check_status':
                return $this->adminCheckStatus($params);

            case 'list_emails':
                return $this->adminListEmails($params);

            case 'list_domains':
                return $this->adminListDomains($params);

            default:
                return parent::adminCustomAction($action, $params);
        }
    }

    /**
     * List the email accounts for a hosting user on CWP.
     *
     * CWP API: POST /email with action=list, user (api.php:146-150).
     *
     * @param  array  $params  Parameters (username required)
     * @return array Operation result
     */
    private function adminListEmails(array $params): array
    {
        if (empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        $response = $this->callApi('email', [
            'action' => 'list',
            'user' => $params['username'],
        ], $params);

        if (! $response['success']) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_list_emails_failed'),
                'data' => $response['data'],
            ];
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.cwp_list_emails_success'),
            'data' => $response['data']['msj'] ?? $response['data'],
        ];
    }

    /**
     * List the domains hosted by a CWP user.
     *
     * CWP API: POST /admindomains with action=list, user (api.php:164-169).
     *
     * @param  array  $params  Parameters (username required)
     * @return array Operation result
     */
    private function adminListDomains(array $params): array
    {
        if (empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        $response = $this->callApi('admindomains', [
            'action' => 'list',
            'user' => $params['username'],
        ], $params);

        if (! $response['success']) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_list_domains_failed'),
                'data' => $response['data'],
            ];
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.cwp_list_domains_success'),
            'data' => $response['data']['msj'] ?? $response['data'],
        ];
    }

    /**
     * List available packages on the CWP server
     *
     * CWP API: POST /packages with action=list
     *
     * @return array Operation result
     */
    private function adminListPackages(array $params = []): array
    {
        $postData = [
            'action' => 'list',
        ];

        $response = $this->callApi('packages', $postData, $params);

        if (! $response['success']) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_list_packages_failed'),
                'data' => $response['data'],
            ];
        }

        $packages = [];
        $packageData = $response['data']['msj'] ?? $response['data'];

        if (is_array($packageData)) {
            foreach ($packageData as $pkg) {
                $packages[] = [
                    'name' => $pkg['package_name'] ?? $pkg['name'] ?? '',
                    'quota' => $pkg['disk_quota'] ?? $pkg['quota'] ?? 'unlimited',
                    'bandwidth' => $pkg['bandwidth'] ?? 'unlimited',
                    'domains' => $pkg['domains'] ?? 'unlimited',
                    'email' => $pkg['email_accounts'] ?? $pkg['email'] ?? 'unlimited',
                    'databases' => $pkg['databases'] ?? $pkg['mysql'] ?? 'unlimited',
                ];
            }
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.cwp_list_packages_success'),
            'data' => [
                'packages' => $packages,
                'count' => count($packages),
            ],
        ];
    }

    /**
     * Get account details for a specific user from CWP
     *
     * CWP API: POST /accountdetail with action=list, user (api.php:116-118).
     * `account/list` returns ALL accounts and ignores `user`; the per-user
     * detail record is only returned by the `accountdetail` endpoint.
     *
     * @param  array  $params  Parameters (username required)
     * @return array Operation result
     */
    private function adminAccountDetails(array $params): array
    {
        if (empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        $postData = [
            'action' => 'list',
            'user' => $params['username'],
        ];

        $response = $this->callApi('accountdetail', $postData, $params);

        if (! $response['success']) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_account_details_failed'),
                'data' => $response['data'],
            ];
        }

        $accountData = $response['data']['msj'] ?? $response['data'];

        return [
            'status' => 'success',
            'message' => __('admin/modules.cwp_account_details_success'),
            'data' => $accountData,
        ];
    }

    /**
     * Check the status of a specific account on CWP
     *
     * CWP API: POST /accountdetail with action=list, user (api.php:116-118).
     * Must target `accountdetail` (not `account/list`) so the per-user
     * `suspended`/`status` field is actually present — otherwise every
     * account is reported as "active" (false-positive).
     *
     * @param  array  $params  Parameters (username required)
     * @return array Operation result
     */
    private function adminCheckStatus(array $params): array
    {
        if (empty($params['username'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_username_required'),
            ];
        }

        $postData = [
            'action' => 'list',
            'user' => $params['username'],
        ];

        $response = $this->callApi('accountdetail', $postData, $params);

        if (! $response['success']) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_check_status_failed'),
                'data' => $response['data'],
            ];
        }

        $accountData = $response['data']['msj'] ?? $response['data'];
        $isSuspended = false;

        if (is_array($accountData)) {
            $isSuspended = ! empty($accountData['suspended']) || ($accountData['status'] ?? '') === 'suspended';
        }

        return [
            'status' => 'success',
            'message' => $isSuspended
                ? __('admin/modules.cwp_account_status_suspended')
                : __('admin/modules.cwp_account_status_active'),
            'data' => [
                'suspended' => $isSuspended,
                'account' => $accountData,
            ],
        ];
    }

    /**
     * Return client area actions
     *
     * @return array Client action definitions
     */
    public function getClientActions(): array
    {
        $actions = parent::getClientActions();

        $actions['login_panel'] = [
            'label' => __('admin/modules.cwp_action_login_panel'),
            'icon' => 'fas fa-sign-in-alt',
            'class' => 'btn-outline-primary',
            'enabled' => true,
        ];

        $actions['change_password'] = [
            'label' => __('admin/modules.cwp_action_change_password'),
            'icon' => 'fas fa-key',
            'class' => 'btn-outline-warning',
            'enabled' => true,
        ];

        return $actions;
    }

    /**
     * Handle client area actions
     *
     * @param  string  $action  Action name
     * @param  array  $params  Parameters
     * @return array Operation result
     */
    public function handleClientAction(string $action, array $params): array
    {
        switch ($action) {
            case 'login_panel':
                return $this->clientLoginPanel($params);

            case 'change_password':
                return $this->changeAccountPassword($params);

            default:
                return parent::handleClientAction($action, $params);
        }
    }

    /**
     * Build the end-user CWP panel login URL.
     *
     * Attempts an SSO session via the CWP `user_session` endpoint
     * (api.php:94-95 — `user_session/list` with a `user` param). CWP's
     * SSO response shape is not publicly documented (the mapping lives in
     * the ionCube-encoded WiseCP module), so we probe the common URL/token
     * keys and ALWAYS fall back to the plain end-user panel URL
     * (`https://{host}:2083`) if no session URL is returned — behaviour never
     * regresses below a direct panel link.
     *
     * @param  array  $params  Parameters (username required for SSO)
     * @return array Operation result (contains `url`)
     */
    private function clientLoginPanel(array $params): array
    {
        $server = $this->serverConfig($params);
        $protocol = $server['secure'] ? 'https' : 'http';
        $hostname = $server['hostname'];

        if (empty($hostname)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.cwp_config_hostname_missing'),
            ];
        }

        // End-user CWP panel listens on 2083 (SSL) / 2082 (plain), distinct
        // from the admin API port (2304). Direct-link fallback.
        $userPort = $server['secure'] ? 2083 : 2082;
        $fallbackUrl = "{$protocol}://{$hostname}:{$userPort}";

        $username = $params['username'] ?? '';

        if (! empty($username)) {
            $response = $this->callApi('user_session', [
                'action' => 'list',
                'user' => $username,
            ], $params);

            if ($response['success'] && is_array($response['data'])) {
                $data = $response['data'];
                $sessionUrl = $data['url']
                    ?? $data['login_url']
                    ?? $data['msj']['url']
                    ?? (is_string($data['msj'] ?? null) && filter_var($data['msj'], FILTER_VALIDATE_URL) ? $data['msj'] : null);

                if (is_string($sessionUrl) && filter_var($sessionUrl, FILTER_VALIDATE_URL)) {
                    return [
                        'status' => 'success',
                        'message' => __('admin/modules.cwp_action_login_panel_success'),
                        'url' => $sessionUrl,
                    ];
                }
            }
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.cwp_action_login_panel_success'),
            'url' => $fallbackUrl,
        ];
    }

    /**
     * Get resource usage for a hosting account on CWP
     *
     * CWP API: POST /accountquota with action=list and a capital-`User`
     * param (api.php:119-121). `account/list` returns all accounts and
     * exposes no per-user quota/usage; `accountquota` is the usage endpoint.
     * Parses disk, bandwidth, domains, email accounts, and databases with
     * tolerant key fallbacks (the exact CWP quota field names are not
     * publicly documented — see audit UNVERIFIED note).
     *
     * @param  array  $params  Account parameters (username required)
     * @return array|null Resource usage data or null on failure
     */
    public function getResourceUsage(array $params): ?array
    {
        if (! isset($params['username']) || empty($params['username'])) {
            return null;
        }

        $postData = [
            'action' => 'list',
            'User' => $params['username'],
        ];

        $response = $this->callApi('accountquota', $postData, $params);

        if (! $response['success'] || empty($response['data'])) {
            return null;
        }

        $accountData = $response['data']['msj'] ?? $response['data'];

        if (! is_array($accountData)) {
            return null;
        }

        $resources = [];

        // Disk usage
        $diskUsed = (int) ($accountData['disk_used'] ?? $accountData['diskused'] ?? 0);
        $diskLimit = $accountData['disk_limit'] ?? $accountData['disklimit'] ?? $accountData['quota'] ?? 'unlimited';

        if (strtolower((string) $diskLimit) === 'unlimited' || $diskLimit === '0') {
            $resources[] = [
                'name' => 'Disk Space',
                'used' => $diskUsed,
                'limit' => 0,
                'unit' => 'MB',
                'percentage' => 0,
            ];
        } else {
            $diskLimit = (int) $diskLimit;
            $resources[] = [
                'name' => 'Disk Space',
                'used' => $diskUsed,
                'limit' => $diskLimit,
                'unit' => 'MB',
                'percentage' => $diskLimit > 0 ? round(($diskUsed / $diskLimit) * 100, 2) : 0,
            ];
        }

        // Bandwidth usage
        $bwUsed = (int) ($accountData['bandwidth_used'] ?? $accountData['bwused'] ?? 0);
        $bwLimit = $accountData['bandwidth_limit'] ?? $accountData['bwlimit'] ?? $accountData['bandwidth'] ?? 'unlimited';

        if (strtolower((string) $bwLimit) === 'unlimited' || $bwLimit === '0') {
            $resources[] = [
                'name' => 'Bandwidth',
                'used' => $bwUsed,
                'limit' => 0,
                'unit' => 'MB',
                'percentage' => 0,
            ];
        } else {
            $bwLimit = (int) $bwLimit;
            $resources[] = [
                'name' => 'Bandwidth',
                'used' => $bwUsed,
                'limit' => $bwLimit,
                'unit' => 'MB',
                'percentage' => $bwLimit > 0 ? round(($bwUsed / $bwLimit) * 100, 2) : 0,
            ];
        }

        // Addon domains
        $domainsUsed = (int) ($accountData['domains_used'] ?? $accountData['addon_domains'] ?? 0);
        $domainsLimit = $accountData['domains_limit'] ?? $accountData['maxdomains'] ?? 'unlimited';

        if (strtolower((string) $domainsLimit) === 'unlimited' || $domainsLimit === '0') {
            $resources[] = [
                'name' => 'Addon Domains',
                'used' => $domainsUsed,
                'limit' => 0,
                'unit' => '',
                'percentage' => 0,
            ];
        } else {
            $domainsLimit = (int) $domainsLimit;
            $resources[] = [
                'name' => 'Addon Domains',
                'used' => $domainsUsed,
                'limit' => $domainsLimit,
                'unit' => '',
                'percentage' => $domainsLimit > 0 ? round(($domainsUsed / $domainsLimit) * 100, 2) : 0,
            ];
        }

        // Email accounts
        $emailUsed = (int) ($accountData['email_used'] ?? $accountData['emailaccounts'] ?? 0);
        $emailLimit = $accountData['email_limit'] ?? $accountData['maxemail'] ?? 'unlimited';

        if (strtolower((string) $emailLimit) === 'unlimited' || $emailLimit === '0') {
            $resources[] = [
                'name' => 'Email Accounts',
                'used' => $emailUsed,
                'limit' => 0,
                'unit' => '',
                'percentage' => 0,
            ];
        } else {
            $emailLimit = (int) $emailLimit;
            $resources[] = [
                'name' => 'Email Accounts',
                'used' => $emailUsed,
                'limit' => $emailLimit,
                'unit' => '',
                'percentage' => $emailLimit > 0 ? round(($emailUsed / $emailLimit) * 100, 2) : 0,
            ];
        }

        // Databases
        $dbUsed = (int) ($accountData['databases_used'] ?? $accountData['mysql'] ?? 0);
        $dbLimit = $accountData['databases_limit'] ?? $accountData['maxmysql'] ?? 'unlimited';

        if (strtolower((string) $dbLimit) === 'unlimited' || $dbLimit === '0') {
            $resources[] = [
                'name' => 'Databases',
                'used' => $dbUsed,
                'limit' => 0,
                'unit' => '',
                'percentage' => 0,
            ];
        } else {
            $dbLimit = (int) $dbLimit;
            $resources[] = [
                'name' => 'Databases',
                'used' => $dbUsed,
                'limit' => $dbLimit,
                'unit' => '',
                'percentage' => $dbLimit > 0 ? round(($dbUsed / $dbLimit) * 100, 2) : 0,
            ];
        }

        return $resources;
    }

    /**
     * Validate the module configuration
     *
     * @return bool True if configuration is valid
     *
     * @throws \Exception When configuration is incomplete
     */
    public function validateConfig()
    {
        if (empty($this->config['hostname'])) {
            throw new \Exception(__('admin/modules.cwp_config_hostname_missing'));
        }

        if (empty($this->config['api_key'])) {
            throw new \Exception(__('admin/modules.cwp_config_api_key_missing'));
        }

        return true;
    }

    /**
     * Call the CWP API
     *
     * Sends a POST request to the CWP API v1 endpoint.
     * Authentication is done via API key as a POST parameter.
     *
     * Transport (host/port/ssl/api-key) is resolved via {@see serverConfig()}
     * so the assigned server (`$params['server']`) takes precedence over the
     * module config (PRC-1015). The API key is taken from the server's
     * password field (CWP's per-server API key).
     *
     * @param  string  $endpoint  API endpoint (e.g. 'account', 'packages')
     * @param  array  $postData  POST parameters for the CWP action
     * @param  array  $context  Full module params (server/module_config) for transport resolution
     * @return array Standardised result with success, message, and data keys
     */
    protected function callApi($endpoint, $postData = [], $context = [])
    {
        $postData = is_array($postData) ? $postData : [];
        $context = is_array($context) ? $context : [];
        $server = $this->serverConfig($context);

        $hostname = $server['hostname'];
        $apiKey = $server['password'];

        if (empty($hostname)) {
            return [
                'success' => false,
                'message' => __('admin/modules.cwp_config_hostname_missing'),
                'data' => [],
            ];
        }

        if (empty($apiKey)) {
            return [
                'success' => false,
                'message' => __('admin/modules.cwp_config_api_key_missing'),
                'data' => [],
            ];
        }

        $protocol = $server['secure'] ? 'https' : 'http';
        $port = ! empty($server['port']) ? $server['port'] : $this->apiPort;
        $url = "{$protocol}://{$hostname}:{$port}/v1/{$endpoint}";

        // Add the API key to the POST parameters (per-server auth key).
        $postData['key'] = $apiKey;

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);

        curl_close($ch);

        // cURL-level failure
        if ($response === false) {
            return [
                'success' => false,
                'message' => __('admin/modules.cwp_api_curl_error', ['error' => $curlError]),
                'data' => [],
            ];
        }

        // HTTP-level failure
        if ($httpCode >= 400) {
            return [
                'success' => false,
                'message' => __('admin/modules.cwp_api_http_error', ['code' => $httpCode]),
                'data' => [],
            ];
        }

        // CWP frequently appends PHP notices wrapped in a trailing `<pre>...`
        // block after the JSON body; strip everything from the first `<pre>`
        // before decoding, matching the CWP/WiseCP contract (api.php:68-72).
        $body = $response;
        if (stripos($body, '<pre>') !== false) {
            $body = explode('<pre>', $body)[0];
        }

        // Parse JSON response
        $decoded = json_decode(trim($body), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            // Raw response is intentionally NOT returned/logged — it may contain
            // credentials or PII (PRC-1015 rule 5).
            return [
                'success' => false,
                'message' => __('admin/modules.cwp_api_json_parse_error'),
                'data' => [],
            ];
        }

        // CWP treats any response whose `status != "Error"` (with `msj`) as
        // success; only an explicit Error status is a failure (api.php:78).
        // This avoids mis-flagging endpoints that return a bare data array or
        // omit the `status` key (e.g. packages/accountquota list shapes).
        $status = isset($decoded['status']) ? strtolower((string) $decoded['status']) : '';
        $isSuccess = ! ($status === 'error' || $status === 'fail');

        $message = $decoded['msj'] ?? $decoded['message'] ?? $decoded['msg'] ?? '';

        return [
            'success' => $isSuccess,
            'message' => is_string($message) ? $message : '',
            'data' => $decoded,
        ];
    }
}
