<?php

namespace App\Modules\Product\Apiscp;

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

/**
 * ApisCP (formerly apnscp) product module.
 *
 * Transport: ApisCP's remote API is SOAP over HTTPS. The WSDL lives at
 * `/apnscp.wsdl`, the SOAP location is `/soap?authkey={api_key}` and the
 * namespace URI is `urn:net.apnscp.soap`. Module functions are invoked with
 * underscore naming (`admin_add_site`, `auth_change_password`, ...).
 * Default listener port: 2083 (https) / 2082 (http).
 *
 * PRC-1015 contract:
 *  - host/creds via $this->serverConfig($params) (assigned server first, then
 *    module_config). $params is threaded into the transport; no inline
 *    $this->config['hostname'|'api_key'|...] reads.
 *  - config-option via $this->configOption(); module-config via $this->moduleConfig().
 *  - package/plan flat: $params['plan'] / $params['package'] with fallback.
 *  - Secrets (password / api key / raw SOAP payloads) are never logged or echoed.
 */
class Apiscp extends AbstractProduct
{
    /**
     * Default ApisCP TLS listener port.
     */
    protected int $apiPort = 2083;

    /**
     * Default ApisCP plaintext listener port.
     */
    protected int $apiPortInsecure = 2082;

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

    // =========================================================================
    // ACCOUNT OPERATIONS
    // =========================================================================

    /**
     * Creates a new ApisCP hosting account.
     *
     * SOAP: admin_add_site(domain, username, opts)
     *
     * @param  array  $params  Required: domain, username, password, email
     *                         Plan resolved from $params['plan']/$params['package'].
     */
    public function createAccount(array $params)
    {
        foreach (['domain', 'username', 'password', 'email'] as $param) {
            if (empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.apiscp_param_missing', ['param' => $param]),
                ];
            }
        }

        // Plan/package: flat builder emit (module-config) with legacy fallback.
        $plan = $params['plan']
            ?? $params['package']
            ?? $this->moduleConfig($params, 'plan')
            ?? $this->moduleConfig($params, 'package')
            ?? $this->moduleConfig($params, 'default_plan');

        // ApisCP opts (siteinfo.* / auth.* groups). Email + password + optional plan.
        $opts = [
            'siteinfo.admin_user' => $params['username'],
            'siteinfo.email'      => $params['email'],
            'auth.tpasswd'        => $params['password'],
        ];

        if (! empty($plan)) {
            $opts['siteinfo.plan'] = $plan;
        }

        // Optional dedicated IP from config option (falls back silently when absent).
        $ip = $params['ip'] ?? $this->configOption($params, 'ip');
        if (! empty($ip)) {
            $opts['ipinfo.ipaddrs'] = [$ip];
        }

        $response = $this->callSoap($params, 'admin_add_site', [
            strtolower((string) $params['domain']),
            $params['username'],
            $opts,
        ]);

        if ($this->isSuccess($response)) {
            $result = [
                'status' => 'success',
                'message' => __('admin/modules.apiscp_account_created_success'),
            ];

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

            return $result;
        }

        $result = [
            'status' => 'error',
            'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_account_create_failed')),
        ];

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

        return $result;
    }

    /**
     * Suspends (deactivates) an ApisCP hosting account.
     *
     * SOAP: admin_deactivate_site(site) — signature is single-arg (string $site).
     *
     * @param  array  $params  Required: domain
     */
    public function suspendAccount(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.apiscp_domain_required'),
            ];
        }

        $response = $this->callSoap($params, 'admin_deactivate_site', [
            strtolower((string) $params['domain']),
        ]);

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

        return [
            'status' => 'error',
            'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_account_suspend_failed')),
        ];
    }

    /**
     * Unsuspends (activates) an ApisCP hosting account.
     *
     * SOAP: admin_activate_site(domain)
     *
     * @param  array  $params  Required: domain
     */
    public function unsuspendAccount(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.apiscp_domain_required'),
            ];
        }

        $response = $this->callSoap($params, 'admin_activate_site', [
            strtolower((string) $params['domain']),
        ]);

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

        return [
            'status' => 'error',
            'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_account_unsuspend_failed')),
        ];
    }

    /**
     * Terminates (deletes) an ApisCP hosting account.
     *
     * SOAP: admin_delete_site(site) — signature is single-arg (string $site).
     *
     * @param  array  $params  Required: domain
     */
    public function terminateAccount(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.apiscp_domain_required'),
            ];
        }

        $response = $this->callSoap($params, 'admin_delete_site', [
            strtolower((string) $params['domain']),
        ]);

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

        return [
            'status' => 'error',
            'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_account_delete_failed')),
        ];
    }

    /**
     * Changes an ApisCP account password.
     *
     * SOAP: auth_change_password(password, username, domain)
     *
     * @param  array  $params  Required: domain, user (or username), password
     */
    public function changeAccountPassword(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.apiscp_domain_required'),
            ];
        }

        $user = $params['user'] ?? $params['username'] ?? null;
        if (empty($user)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.apiscp_username_required'),
            ];
        }

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

        $response = $this->callSoap($params, 'auth_change_password', [
            $params['password'],
            $user,
            strtolower((string) $params['domain']),
        ]);

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

        return [
            'status' => 'error',
            'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_password_change_failed')),
        ];
    }

    // =========================================================================
    // PACKAGE OPERATIONS
    // =========================================================================

    /**
     * Changes the service plan for an ApisCP account.
     *
     * SOAP: admin_edit_site(domain, opts)
     *
     * @param  array  $params  Required: domain; plan from $params['plan']/$params['package'].
     */
    public function changeAccountPackage(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.apiscp_domain_required'),
            ];
        }

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

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

        $response = $this->callSoap($params, 'admin_edit_site', [
            strtolower((string) $params['domain']),
            ['siteinfo.plan' => $plan],
        ]);

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

        return [
            'status' => 'error',
            'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_package_change_failed')),
        ];
    }

    // =========================================================================
    // CONNECTION & CONFIGURATION
    // =========================================================================

    /**
     * Tests the connection to the ApisCP server.
     *
     * SOAP: auth_session_info() — verifies connectivity + API key auth.
     *
     * @param  array  $params  Test parameters (host/creds resolved via serverConfig)
     */
    public function testConnection(array $params = [])
    {
        $response = $this->callSoap($params, 'auth_session_info', []);

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

        return [
            'status' => 'error',
            'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_connection_test_failed')),
        ];
    }

    /**
     * Returns admin panel configuration fields.
     */
    public function adminFields()
    {
        return [
            'hostname' => [
                'name' => __('admin/modules.apiscp_field_hostname'),
                'type' => 'text',
                'description' => __('admin/modules.apiscp_field_hostname_description'),
                'required' => true,
            ],
            'port' => [
                'name' => __('admin/modules.apiscp_field_port'),
                'type' => 'text',
                'description' => __('admin/modules.apiscp_field_port_description'),
                'required' => false,
                'default' => '2083',
            ],
            'admin_user' => [
                'name' => __('admin/modules.apiscp_field_admin_user'),
                'type' => 'text',
                'description' => __('admin/modules.apiscp_field_admin_user_description'),
                'required' => true,
            ],
            'api_key' => [
                'name' => __('admin/modules.apiscp_field_api_key'),
                'type' => 'password',
                'description' => __('admin/modules.apiscp_field_api_key_description'),
                'required' => true,
            ],
            'use_ssl' => [
                'name' => __('admin/modules.apiscp_field_use_ssl'),
                'type' => 'checkbox',
                'description' => __('admin/modules.apiscp_field_use_ssl_description'),
                'default' => true,
            ],
        ];
    }

    /**
     * Returns client panel fields.
     */
    public function clientFields()
    {
        return [
            'domain' => [
                'name' => __('admin/modules.apiscp_field_domain'),
                'type' => 'text',
                'description' => __('admin/modules.apiscp_field_domain_description'),
                'required' => true,
            ],
            'username' => [
                'name' => __('admin/modules.apiscp_field_client_username'),
                'type' => 'text',
                'description' => __('admin/modules.apiscp_field_client_username_description'),
                'required' => true,
            ],
            'password' => [
                'name' => __('admin/modules.apiscp_field_client_password'),
                'type' => 'password',
                'description' => __('admin/modules.apiscp_field_client_password_description'),
                'required' => true,
            ],
        ];
    }

    /**
     * Returns server management fields.
     */
    public function serverFields()
    {
        return [
            'api_key' => [
                'name' => __('admin/modules.apiscp_field_api_key'),
                'type' => 'password',
                'description' => __('admin/modules.apiscp_field_api_key_description'),
                'required' => true,
            ],
        ];
    }

    /**
     * Validates the module configuration.
     *
     * @return bool True if configuration is valid
     *
     * @throws \Exception When configuration is missing required values
     */
    public function validateConfig()
    {
        $conn = $this->serverConfig([]);

        if (empty($conn['hostname'])) {
            throw new \Exception(__('admin/modules.apiscp_config_hostname_missing'));
        }

        if (empty($conn['password'])) {
            throw new \Exception(__('admin/modules.apiscp_config_api_key_missing'));
        }

        return true;
    }

    // =========================================================================
    // ADMIN BUTTONS & ACTIONS
    // =========================================================================

    /**
     * Returns admin panel custom buttons.
     */
    public function getAdminButtons(): array
    {
        return [
            [
                'action' => 'account_details',
                'label' => __('admin/modules.apiscp_action_account_details'),
                'icon' => 'fas fa-info-circle',
                'class' => 'btn-outline-primary',
            ],
            [
                'action' => 'server_info',
                'label' => __('admin/modules.apiscp_action_server_info'),
                'icon' => 'fas fa-server',
                'class' => 'btn-outline-success',
            ],
        ];
    }

    /**
     * Handles admin custom actions.
     *
     * @param  string  $action  Action name (account_details, server_info)
     * @param  array  $params  Parameters
     */
    public function adminCustomAction(string $action, array $params): array
    {
        switch ($action) {
            case 'account_details':
                if (empty($params['domain'])) {
                    return [
                        'status' => 'error',
                        'message' => __('admin/modules.apiscp_domain_required'),
                    ];
                }

                // get_meta_from_domain(string $domain, string $service, string $class = null)
                // — $service is mandatory. 'siteinfo' returns the account's core
                // metadata (admin user/email/plan), matching ApisCP's own usage.
                $response = $this->callSoap($params, 'admin_get_meta_from_domain', [
                    strtolower((string) $params['domain']),
                    'siteinfo',
                ]);

                if (! $this->isSuccess($response)) {
                    return [
                        'status' => 'error',
                        'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_account_details_failed')),
                    ];
                }

                return [
                    'status' => 'success',
                    'message' => __('admin/modules.apiscp_account_details_success'),
                    'data' => $this->resultData($response),
                ];

            case 'server_info':
                $response = $this->callSoap($params, 'auth_session_info', []);

                if (! $this->isSuccess($response)) {
                    return [
                        'status' => 'error',
                        'message' => $this->getErrorMessage($response, __('admin/modules.apiscp_server_info_failed')),
                    ];
                }

                return [
                    'status' => 'success',
                    'message' => __('admin/modules.apiscp_server_info_success'),
                    'data' => $this->resultData($response),
                ];

            default:
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.apiscp_action_not_supported', ['action' => $action]),
                ];
        }
    }

    // =========================================================================
    // CLIENT ACTIONS
    // =========================================================================

    /**
     * Returns client area actions.
     */
    public function getClientActions(): array
    {
        $actions = parent::getClientActions();

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

        return $actions;
    }

    /**
     * Handles client actions.
     *
     * @param  string  $action  Action name
     * @param  array  $params  Parameters
     */
    public function handleClientAction(string $action, array $params): array
    {
        switch ($action) {
            case 'login_panel':
                return $this->createPanelLoginUrl($params);

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

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

    // =========================================================================
    // RESOURCE USAGE
    // =========================================================================

    /**
     * Returns resource usage information for an ApisCP account.
     *
     * ApisCP exposes usage through admin-scoped calls keyed by internal site id
     * (e.g. `site12`), not a per-site quota call:
     *   - admin_collect(["siteinfo.domain"])  → site-id ⇒ ['domain' => ...]
     *   - admin_get_usage("storage")          → site-id ⇒ ['qhard','qused'] (KB)
     *   - admin_get_usage("bandwidth")        → site-id ⇒ ['threshold','sum'] (KB)
     * We resolve the site id for the requested domain, then map storage (KB→MB)
     * and bandwidth (KB→GB). Mirrors the WiseCP ApisCP reference module.
     *
     * @param  array  $params  Parameters (domain required)
     * @return array|null Resource usage data or null
     */
    public function getResourceUsage(array $params): ?array
    {
        if (empty($params['domain'])) {
            return null;
        }

        $domain = strtolower((string) $params['domain']);

        $sitesResp = $this->callSoap($params, 'admin_collect', [['siteinfo.domain']]);
        if (! $this->isSuccess($sitesResp)) {
            return null;
        }

        $sites = $this->resultData($sitesResp);
        if (! is_array($sites)) {
            return null;
        }

        // Resolve the internal site id whose siteinfo.domain matches our domain.
        $siteId = null;
        foreach ($sites as $id => $values) {
            $siteDomain = is_array($values) ? ($values['domain'] ?? null) : null;
            if ($siteDomain !== null && strtolower((string) $siteDomain) === $domain) {
                $siteId = $id;
                break;
            }
        }

        if ($siteId === null) {
            return null;
        }

        $storageResp = $this->callSoap($params, 'admin_get_usage', ['storage']);
        $bandwidthResp = $this->callSoap($params, 'admin_get_usage', ['bandwidth']);

        $storageAll = $this->isSuccess($storageResp) ? $this->resultData($storageResp) : null;
        $bandwidthAll = $this->isSuccess($bandwidthResp) ? $this->resultData($bandwidthResp) : null;

        $storage = (is_array($storageAll) && isset($storageAll[$siteId]) && is_array($storageAll[$siteId]))
            ? $storageAll[$siteId] : [];
        $bandwidth = (is_array($bandwidthAll) && isset($bandwidthAll[$siteId]) && is_array($bandwidthAll[$siteId]))
            ? $bandwidthAll[$siteId] : [];

        $resources = [];

        // Storage: qhard/qused are in KB → MB.
        $diskLimit = (float) ($storage['qhard'] ?? 0);
        $diskUsed = (float) ($storage['qused'] ?? 0);
        $diskLimitMb = round($diskLimit / 1024);
        $diskUsedMb = round($diskUsed / 1024);
        $resources[] = [
            'name' => 'Disk Space',
            'used' => $diskUsedMb,
            'limit' => $diskLimitMb,
            'unit' => 'MB',
            'percentage' => $diskLimitMb > 0 ? round(($diskUsedMb / $diskLimitMb) * 100, 2) : 0,
        ];

        // Bandwidth: threshold/sum are in KB → GB. threshold may be "unlimited".
        $bwThreshold = $bandwidth['threshold'] ?? 0;
        $bwLimitGb = (is_numeric($bwThreshold) && (float) $bwThreshold > 0)
            ? round(((float) $bwThreshold) / 1024 / 1024, 2)
            : 0;
        $bwUsedGb = round(((float) ($bandwidth['sum'] ?? 0)) / 1024 / 1024, 2);
        if ($bwLimitGb > 0 || $bwUsedGb > 0) {
            $resources[] = [
                'name' => 'Bandwidth',
                'used' => $bwUsedGb,
                'limit' => $bwLimitGb,
                'unit' => 'GB',
                'percentage' => $bwLimitGb > 0 ? round(($bwUsedGb / $bwLimitGb) * 100, 2) : 0,
            ];
        }

        return ! empty($resources) ? $resources : null;
    }

    // =========================================================================
    // SOAP TRANSPORT
    // =========================================================================

    /**
     * Invokes an ApisCP SOAP module function.
     *
     * Host/creds come from $this->serverConfig($params): the assigned server
     * takes priority, otherwise module_config. The API key (password) is
     * passed as the SOAP `authkey` and NEVER logged.
     *
     * @param  array  $params  Enriched module params (server/module_config context)
     * @param  string  $method  SOAP function, e.g. admin_add_site
     * @param  array  $args  Positional arguments for the SOAP call
     * @param  string|null  $impersonateDomain  Run the call in a site's context (X-Forwarded-For style)
     * @return array Normalised response: ['status' => bool, 'data' => mixed] or ['status' => false, 'error_message' => string]
     */
    protected function callSoap(array $params, string $method, array $args = [], ?string $impersonateDomain = null): array
    {
        if (! class_exists('SoapClient')) {
            return [
                'status' => false,
                'error_message' => __('admin/modules.apiscp_soap_ext_missing'),
            ];
        }

        $conn = $this->serverConfig($params);

        if (empty($conn['hostname']) || empty($conn['password'])) {
            return [
                'status' => false,
                'error_message' => __('admin/modules.apiscp_config_incomplete'),
            ];
        }

        $secure = (bool) $conn['secure'];
        $protocol = $secure ? 'https' : 'http';
        $port = ! empty($conn['port']) ? (int) $conn['port'] : ($secure ? $this->apiPort : $this->apiPortInsecure);
        $endpoint = "{$protocol}://{$conn['hostname']}:{$port}";
        $apiKey = (string) $conn['password'];

        // TLS is verified. ApisCP ships a valid cert on the panel host; disabling
        // verification would defeat the authkey-over-TLS security model.
        $streamHeaders = [];
        if ($impersonateDomain !== null && $impersonateDomain !== '') {
            $streamHeaders[] = 'X-Forwarded-For: ' . $impersonateDomain;
        }

        $contextOptions = [
            'ssl' => [
                'verify_peer'      => $secure,
                'verify_peer_name' => $secure,
            ],
        ];
        if (! empty($streamHeaders)) {
            $contextOptions['http'] = ['header' => implode("\r\n", $streamHeaders)];
        }

        try {
            $client = new \SoapClient($endpoint . '/apnscp.wsdl', [
                'connection_timeout' => 30,
                'location'           => $endpoint . '/soap?authkey=' . rawurlencode($apiKey),
                'uri'                => 'urn:net.apnscp.soap',
                'trace'              => false,
                'exceptions'         => true,
                'stream_context'     => stream_context_create($contextOptions),
            ]);

            $result = call_user_func_array([$client, $method], $args);

            return [
                'status' => true,
                'data'   => $result,
            ];
        } catch (\SoapFault $e) {
            return [
                'status' => false,
                'error_message' => $this->sanitizeFault($e->getMessage()),
            ];
        } catch (\Throwable $e) {
            return [
                'status' => false,
                'error_message' => $this->sanitizeFault($e->getMessage()),
            ];
        }
    }

    // =========================================================================
    // HELPER METHODS
    // =========================================================================

    /**
     * Whether a normalised SOAP response indicates success.
     */
    protected function isSuccess(array $response): bool
    {
        return isset($response['status']) && $response['status'] === true;
    }

    /**
     * Extracts the payload from a normalised SOAP response.
     *
     * @return mixed
     */
    protected function resultData(array $response)
    {
        return $response['data'] ?? null;
    }

    /**
     * Extracts an error message from a normalised SOAP response.
     */
    protected function getErrorMessage(array $response, string $fallback): string
    {
        $msg = $response['error_message'] ?? null;

        return (is_string($msg) && $msg !== '') ? $msg : $fallback;
    }

    /**
     * Strips any authkey query fragment from a SOAP fault message so credentials
     * never surface in UI/error output.
     */
    protected function sanitizeFault(string $message): string
    {
        return (string) preg_replace('/authkey=[^\s&"\']+/i', 'authkey=***', $message);
    }

    /**
     * Creates a single sign-on login into the ApisCP panel.
     *
     * ApisCP hands out a transferable session token via admin_hijack(domain,
     * user, "UI"); the panel then honours it as the `esprit_id` query on the
     * dashboard app. This is the genuine SSO path (session transfer), matching
     * the WiseCP ApisCP reference module. `createPanelLoginUrl` delegates here.
     *
     * @param  array  $params  Parameters (domain + user/username)
     */
    public function createSSOLogin(array $params)
    {
        return $this->createPanelLoginUrl($params);
    }

    /**
     * Builds a genuine SSO login URL for the client via admin_hijack.
     *
     * admin_hijack(domain, user, "UI") returns an esprit_id session token which
     * is appended to the dashboard app URL: {endpoint}/apps/dashboard?esprit_id=…
     * If the panel does not return a token (older builds / restricted API key),
     * we fall back to the plain panel URL so the action still degrades usefully.
     *
     * @param  array  $params  Parameters (domain + user/username)
     */
    protected function createPanelLoginUrl(array $params): array
    {
        $conn = $this->serverConfig($params);

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

        $secure = (bool) $conn['secure'];
        $protocol = $secure ? 'https' : 'http';
        $port = ! empty($conn['port']) ? (int) $conn['port'] : ($secure ? $this->apiPort : $this->apiPortInsecure);
        $endpoint = "{$protocol}://{$conn['hostname']}:{$port}";

        $domain = ! empty($params['domain']) ? strtolower((string) $params['domain']) : null;
        $user = $params['user'] ?? $params['username'] ?? null;

        // Genuine SSO: hijack the site admin's session and hand the client the
        // dashboard URL carrying the esprit_id token.
        if ($domain !== null && ! empty($user)) {
            $response = $this->callSoap($params, 'admin_hijack', [$domain, $user, 'UI']);

            if ($this->isSuccess($response)) {
                $token = $this->resultData($response);
                if (is_string($token) && $token !== '') {
                    $ssoUrl = $endpoint . '/apps/dashboard?' . http_build_query(['esprit_id' => $token]);

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

        // Fallback: no transferable token — return the panel base URL so the
        // client can authenticate with their own credentials.
        return [
            'status' => 'success',
            'message' => __('admin/modules.apiscp_login_panel_success'),
            'url' => $endpoint,
        ];
    }
}
