<?php

/**
 * WHMCS Server Module for Onoxsoft Panel
 *
 * Compatible: WHMCS 8.x and 9.x
 * PHP:        7.4+
 * Author:     Onoxsoft Software <dev@onox.com.tr>
 * Website:    https://onox.com.tr
 * License:    Proprietary
 *
 * Required hooks implemented:
 *   MetaData, ConfigOptions, TestConnection,
 *   CreateAccount, SuspendAccount, UnsuspendAccount, TerminateAccount,
 *   ChangePassword, ChangePackage, UsageUpdate,
 *   ClientArea, AdminLink, LoginLink
 */
if (! defined('WHMCS')) {
    exit('This file cannot be accessed directly');
}

use OnoxWhmcs\ApiClient;
use OnoxWhmcs\ConfigOptions;
use OnoxWhmcs\ErrorHandler;
use OnoxWhmcs\Helper;
use WHMCS\Database\Capsule;

require_once __DIR__.'/lib/ApiClient.php';
require_once __DIR__.'/lib/ConfigOptions.php';
require_once __DIR__.'/lib/ErrorHandler.php';
require_once __DIR__.'/lib/Helper.php';

// ─── Metadata ──────────────────────────────────────────────────────────────

/**
 * Metadata about the module.
 */
function onox_MetaData(): array
{
    return [
        'DisplayName' => 'Onoxsoft Panel',
        'APIVersion' => '1.1',
        'RequiresServer' => true,
        'DefaultNonSSLPort' => '80',
        'DefaultSSLPort' => '443',
        'ServiceSingleSignOnLabel' => 'Panele Giriş',
        'AdminSingleSignOnLabel' => 'Panele Giriş (Admin)',
    ];
}

// ─── Config Options ─────────────────────────────────────────────────────────

/**
 * Config options shown on the WHMCS product/service admin page.
 */
function onox_ConfigOptions(): array
{
    return ConfigOptions::schema();
}

// ─── Connection Test ────────────────────────────────────────────────────────

/**
 * Tests connectivity to the Onoxsoft panel.
 * Called from: WHMCS Admin → Setup → Servers → Test Connection.
 *
 * @param  array  $params  WHMCS server params
 * @return array{success: bool, error: string}
 */
function onox_TestConnection(array $params): array
{
    try {
        $api = new ApiClient($params);
        $health = $api->get('/health');

        $version = $health['version'] ?? 'unknown';

        return [
            'success' => true,
            'error' => '',
        ];
    } catch (Throwable $e) {
        ErrorHandler::log('TestConnection', $params, $e);

        return [
            'success' => false,
            'error' => $e->getMessage(),
        ];
    }
}

// ─── Account Lifecycle ──────────────────────────────────────────────────────

/**
 * Provision a new hosting account.
 *
 * Onoxsoft endpoint: POST /api/v1/whmcs/account
 * On success stores Onoxsoft account ID into tblhosting.subscriptionid.
 *
 * @return string 'success' or error message
 */
function onox_CreateAccount(array $params): string
{
    try {
        $api = new ApiClient($params);
        $config = ConfigOptions::resolve($params);

        $payload = [
            'email' => $params['clientsdetails']['email'],
            'owner_email' => $params['clientsdetails']['email'],
            'domain' => $params['domain'],
            'package_id' => $config['package_slug'],
            'options' => [
                'auto_create_dns' => $config['auto_create_dns'],
                'auto_provision_ssl' => $config['auto_provision_ssl'],
                'send_welcome_email' => $config['send_welcome_email'],
            ],
            'whmcs_service_id' => $params['serviceid'],
        ];

        $response = $api->post('/whmcs/account', $payload, [
            'X-Idempotency-Key' => 'whmcs-create-'.$params['serviceid'],
        ]);

        // Persist Onoxsoft account reference in WHMCS hosting row
        Capsule::table('tblhosting')
            ->where('id', $params['serviceid'])
            ->update([
                'username' => $response['username'] ?? '',
                'password' => encrypt($response['initial_password'] ?? ''),
                'subscriptionid' => (string) ($response['account_id'] ?? ''),
            ]);

        return 'success';
    } catch (Throwable $e) {
        ErrorHandler::log('CreateAccount', $params, $e);

        return Helper::lang('error_prefix').$e->getMessage();
    }
}

/**
 * Suspend a hosting account.
 *
 * Onoxsoft endpoint: PATCH /api/v1/whmcs/account/{id}/suspend
 *
 * @return string 'success' or error message
 */
function onox_SuspendAccount(array $params): string
{
    try {
        $api = new ApiClient($params);
        $accountId = Helper::resolveAccountId($params);
        $reason = $params['suspendreason'] ?? 'Suspended via WHMCS';

        $api->patch("/whmcs/account/{$accountId}/suspend", ['reason' => $reason], [
            'X-Idempotency-Key' => 'whmcs-suspend-'.$params['serviceid'],
        ]);

        return 'success';
    } catch (Throwable $e) {
        ErrorHandler::log('SuspendAccount', $params, $e);

        return Helper::lang('error_prefix').$e->getMessage();
    }
}

/**
 * Unsuspend a hosting account.
 *
 * Onoxsoft endpoint: PATCH /api/v1/whmcs/account/{id}/unsuspend
 *
 * @return string 'success' or error message
 */
function onox_UnsuspendAccount(array $params): string
{
    try {
        $api = new ApiClient($params);
        $accountId = Helper::resolveAccountId($params);

        $api->patch("/whmcs/account/{$accountId}/unsuspend", [], [
            'X-Idempotency-Key' => 'whmcs-unsuspend-'.$params['serviceid'],
        ]);

        return 'success';
    } catch (Throwable $e) {
        ErrorHandler::log('UnsuspendAccount', $params, $e);

        return Helper::lang('error_prefix').$e->getMessage();
    }
}

/**
 * Terminate (delete) a hosting account.
 *
 * Onoxsoft endpoint: DELETE /api/v1/whmcs/account/{id}
 *
 * @return string 'success' or error message
 */
function onox_TerminateAccount(array $params): string
{
    try {
        $api = new ApiClient($params);
        $accountId = Helper::resolveAccountId($params);

        $api->delete("/whmcs/account/{$accountId}", ['delete_data' => true], [
            'X-Idempotency-Key' => 'whmcs-terminate-'.$params['serviceid'],
        ]);

        // Clear stored reference
        Capsule::table('tblhosting')
            ->where('id', $params['serviceid'])
            ->update(['subscriptionid' => '']);

        return 'success';
    } catch (Throwable $e) {
        ErrorHandler::log('TerminateAccount', $params, $e);

        return Helper::lang('error_prefix').$e->getMessage();
    }
}

/**
 * Change the cPanel/hosting password.
 *
 * Onoxsoft endpoint: POST /api/v1/accounts/{id}/sso  (token-based reset)
 * We generate a password-reset token via SSO and then call a dedicated
 * change-password endpoint.
 *
 * @return string 'success' or error message
 */
function onox_ChangePassword(array $params): string
{
    try {
        $api = new ApiClient($params);
        $accountId = Helper::resolveAccountId($params);
        $newPass = $params['password'];

        $api->patch("/whmcs/account/{$accountId}/password", [
            'password' => $newPass,
        ], [
            'X-Idempotency-Key' => 'whmcs-chpass-'.$params['serviceid'].'-'.substr(md5($newPass), 0, 8),
        ]);

        // Update stored encrypted password in WHMCS
        Capsule::table('tblhosting')
            ->where('id', $params['serviceid'])
            ->update(['password' => encrypt($newPass)]);

        return 'success';
    } catch (Throwable $e) {
        ErrorHandler::log('ChangePassword', $params, $e);

        return Helper::lang('error_prefix').$e->getMessage();
    }
}

/**
 * Upgrade / downgrade hosting package.
 *
 * Onoxsoft endpoint: PATCH /api/v1/whmcs/account/{id}/package
 *
 * @return string 'success' or error message
 */
function onox_ChangePackage(array $params): string
{
    try {
        $api = new ApiClient($params);
        $accountId = Helper::resolveAccountId($params);
        $config = ConfigOptions::resolve($params);

        $api->patch("/whmcs/account/{$accountId}/package", [
            'package_id' => $config['package_slug'],
        ], [
            'X-Idempotency-Key' => 'whmcs-chpkg-'.$params['serviceid'].'-'.$config['package_slug'],
        ]);

        return 'success';
    } catch (Throwable $e) {
        ErrorHandler::log('ChangePackage', $params, $e);

        return Helper::lang('error_prefix').$e->getMessage();
    }
}

// ─── Usage Update (cron) ────────────────────────────────────────────────────

/**
 * Bulk usage update called by WHMCS UsageUpdate cron.
 *
 * Onoxsoft endpoint: GET /api/v1/whmcs/usage-batch
 *
 * @return string 'success' or error message
 */
function onox_UsageUpdate(array $params): string
{
    try {
        $api = new ApiClient($params);

        // Collect Onoxsoft account IDs from service rows
        $serviceIds = array_keys($params['products'] ?? []);
        if (empty($serviceIds)) {
            return 'success';
        }

        // Fetch subscriptionid → serviceId map from tblhosting
        $rows = Capsule::table('tblhosting')
            ->whereIn('id', $serviceIds)
            ->whereNotNull('subscriptionid')
            ->where('subscriptionid', '!=', '')
            ->get(['id', 'subscriptionid']);

        $accountIdToServiceId = [];
        foreach ($rows as $row) {
            $accountIdToServiceId[$row->subscriptionid] = $row->id;
        }

        if (empty($accountIdToServiceId)) {
            return 'success';
        }

        $usageData = $api->get('/whmcs/usage-batch', [
            'account_ids' => implode(',', array_keys($accountIdToServiceId)),
        ]);

        foreach ($usageData['data'] ?? [] as $item) {
            $onoxId = (string) $item['account_id'];
            $serviceId = $accountIdToServiceId[$onoxId] ?? null;
            if (! $serviceId) {
                continue;
            }

            Capsule::table('tblhosting')
                ->where('id', $serviceId)
                ->update([
                    'diskused' => (int) round(($item['disk_usage_bytes'] ?? 0) / 1024 / 1024),
                    'disklimit' => (int) round(($item['disk_quota_bytes'] ?? 0) / 1024 / 1024),
                    'bwusage' => (int) round(($item['bandwidth_used_bytes'] ?? 0) / 1024 / 1024),
                    'bwlimit' => (int) round(($item['bandwidth_limit_bytes'] ?? 0) / 1024 / 1024),
                    'lastupdate' => date('Y-m-d H:i:s'),
                ]);
        }

        return 'success';
    } catch (Throwable $e) {
        ErrorHandler::log('UsageUpdate', $params, $e);

        return Helper::lang('error_prefix').$e->getMessage();
    }
}

// ─── SSO / Panel Links ──────────────────────────────────────────────────────

/**
 * Customer area widget — renders an "Open Panel" button.
 *
 * @return array Smarty template file + vars
 */
function onox_ClientArea(array $params): array
{
    $vars = [
        'username' => $params['model']->username ?? '',
        'panel_url' => '',
        'error' => '',
    ];

    try {
        $api = new ApiClient($params);
        $accountId = Helper::resolveAccountId($params);
        $vars['panel_url'] = $api->generateSsoUrl($accountId, 'customer');
    } catch (Throwable $e) {
        $vars['error'] = $e->getMessage();
    }

    return [
        'templatefile' => 'client_area',
        'vars' => $vars,
    ];
}

/**
 * Admin shortcut button on service detail page.
 *
 * @return string HTML anchor tag
 */
function onox_AdminLink(array $params): string
{
    try {
        $api = new ApiClient($params);
        $accountId = Helper::resolveAccountId($params);
        $loginUrl = $api->generateSsoUrl($accountId, 'admin');

        return '<a href="'.htmlspecialchars($loginUrl, ENT_QUOTES, 'UTF-8').'" '
             .'target="_blank" class="btn btn-primary btn-sm">'
             .Helper::lang('admin_login_button')
             .'</a>';
    } catch (Throwable $e) {
        return '<span class="text-danger">'.htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8').'</span>';
    }
}

/**
 * Single-sign-on link — used by WHMCS "Login as Client" feature.
 *
 * @return array{url: string, target: string}
 */
function onox_LoginLink(array $params): array
{
    try {
        $api = new ApiClient($params);
        $accountId = Helper::resolveAccountId($params);
        $url = $api->generateSsoUrl($accountId, 'customer');

        return ['url' => $url, 'target' => '_blank'];
    } catch (Throwable $e) {
        ErrorHandler::log('LoginLink', $params, $e);

        return ['url' => '#', 'target' => '_self'];
    }
}
