<?php

namespace App\Modules\Registrar\Resellercamp;

use App\Models\Registrar;
use App\Modules\Registrar\ResellerClub\ResellerClub;
use Illuminate\Support\Facades\Log;

/**
 * ResellerCamp is a LogicBoxes/ResellerClub reseller on the shared httpapi.com
 * transport. The base transport/child-NS/sync/EPP behaviour is inherited from
 * ResellerClub; this subclass overrides only the endpoint + the register/
 * transfer/renew/contact/availability/details paths that require WHMCS-parity
 * TLD-specific handling (idnLanguageCode, .uk/.eu/.nz/.ru/.ca/.nl/.london
 * -1 contacts, .cn/.au/.es/.asia/.us/.pro/.nl/.tel attr-name/value pairs,
 * IRTP designated-agent, change-detection). References: WHMCS 7.8
 * resellerclub.php RegisterDomain:166 / TransferDomain:291 / RenewDomain:389 /
 * SaveContactDetails:522 / GetContactDetails:455 / CheckAvailability:2280 /
 * GetDomainInformation:2485 / ContactTLDSpecificFields:1407 /
 * DomainTLDSpecificFields:1587.
 */
class Resellercamp extends ResellerClub
{
    /**
     * ResellerCamp uses the same LogicBoxes API via its own endpoint.
     */
    protected $apiEndpoint = 'https://httpapi.com/api/';

    /**
     * Test API endpoint
     */
    protected $testApiEndpoint = 'https://test.httpapi.com/api/';

    public function __construct(array $config = [], ?Registrar $registrarModel = null)
    {
        parent::__construct($config, $registrarModel);
    }

    public function getModuleInfo(): array
    {
        $defaultInfo = parent::getModuleInfo();

        return array_merge($defaultInfo, [
            'label' => __('admin/modules.resellercamp_module_label'),
            'description' => __('admin/modules.resellercamp_module_description'),
        ]);
    }

    public function getConfigFields(): array
    {
        return [
            'reseller_id' => [
                'type' => 'text',
                'label' => __('admin/modules.resellercamp_field_reseller_id_label'),
                'required' => true,
                'description' => __('admin/modules.resellercamp_field_reseller_id_description'),
                'placeholder' => '123456',
                'value' => $this->config['reseller_id'] ?? '',
            ],
            'api_key' => [
                'type' => 'password',
                'label' => __('admin/modules.resellercamp_field_api_key_label'),
                'required' => true,
                'description' => __('admin/modules.resellercamp_field_api_key_description'),
                'placeholder' => '********',
                'value' => $this->config['api_key'] ?? '',
            ],
            'test_mode' => [
                'type' => 'checkbox',
                'label' => __('admin/modules.resellercamp_field_test_mode_label'),
                'required' => false,
                'description' => __('admin/modules.resellercamp_field_test_mode_description'),
                'value' => $this->config['test_mode'] ?? false,
            ],
            'enable_premium_domains' => [
                'type' => 'checkbox',
                'label' => __('admin/modules.resellercamp_field_premium_domains_label'),
                'required' => false,
                'description' => __('admin/modules.resellercamp_field_premium_domains_description'),
                'value' => $this->config['enable_premium_domains'] ?? false,
            ],
        ];
    }

    // -------------------------------------------------------------------------
    //  Domain Registration (WHMCS resellerclub_RegisterDomain:166)
    // -------------------------------------------------------------------------

    public function registerDomain(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.resellerclub_domain_required'));
        }

        try {
            $this->validateConfig();

            $tld = $this->extractTld($domain);
            $additional = $params['additional_fields'] ?? [];

            $customerId = $this->getOrCreateCustomer($params);
            if (is_array($customerId) && ($customerId['status'] ?? '') === 'error') {
                return $customerId;
            }

            $contactId = $this->getOrCreateContact($params, $customerId);
            if (is_array($contactId) && ($contactId['status'] ?? '') === 'error') {
                return $contactId;
            }

            // Push TLD-specific CONTACT attributes onto the registrant contact
            // via set-details (WHMCS resellerclub.php:210-218).
            $contactFields = $this->buildContactTldFields($tld, $params, $additional);
            if (!empty($contactFields) && !empty($contactFields['product-key'])) {
                $this->sendCommand('set-details', 'contacts', array_merge([
                    'customer-id' => $customerId,
                    'contact-id' => $contactId,
                ], $contactFields), 'POST');
            }

            $requestData = [
                'domain-name' => $domain,
                'years' => $params['period'] ?? $params['years'] ?? 1,
                'ns' => $this->buildNameserverList($params),
                'customer-id' => $customerId,
                'reg-contact-id' => $contactId,
                'admin-contact-id' => $params['admin_contact_id'] ?? $contactId,
                'tech-contact-id' => $params['tech_contact_id'] ?? $contactId,
                'billing-contact-id' => $params['billing_contact_id'] ?? $contactId,
                'invoice-option' => 'NoInvoice',
                'purchase-privacy' => (!empty($params['id_protection']) || !empty($params['privacy'])) ? 'true' : 'false',
            ];

            // idnLanguageCode (WHMCS resellerclub.php:229-234)
            $requestData = array_merge($requestData, $this->buildIdnLanguageField($additional));

            // TLD-specific DOMAIN attributes + -1 contact overrides + premium
            // (WHMCS resellerclub.php:259 + DomainTLDSpecificFields:1587).
            $isPremium = !empty($params['is_premium']) && !empty($this->config['enable_premium_domains']);
            $requestData = array_merge(
                $requestData,
                $this->buildDomainTldFields($tld, $params, $additional, $contactId, $isPremium)
            );

            // .au ABN sanity (WHMCS resellerclub.php:260-262)
            if ($tld === 'au'
                && isset($requestData['attr-value2'])
                && is_numeric($requestData['attr-value2'])
                && !$this->validateAbn($requestData['attr-value2'])) {
                return $this->errorResponse(__('admin/modules.resellerclub_register_failed'));
            }

            $result = $this->sendCommand('register', 'domains', $requestData, 'POST');

            if ($this->isApiError($result)) {
                return $this->errorResponse($this->getApiErrorMessage($result));
            }

            if (isset($result['actionstatus']) && $result['actionstatus'] === 'Failed') {
                return $this->errorResponse($result['actionstatusdesc'] ?? __('admin/modules.resellerclub_register_failed'));
            }

            Log::info('Resellercamp: Domain registered', [
                'domain' => $domain,
                'order_id' => $result['entityid'] ?? null,
            ]);

            return $this->successResponse(__('admin/modules.resellerclub_register_success'), [
                'domain' => $domain,
                'order_id' => $result['entityid'] ?? null,
                'period' => $params['period'] ?? $params['years'] ?? 1,
            ]);
        } catch (\Exception $e) {
            Log::error('Resellercamp: Register domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Domain Transfer (WHMCS resellerclub_TransferDomain:291)
    // -------------------------------------------------------------------------

    public function transferDomain(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.resellerclub_domain_required'));
        }

        try {
            $this->validateConfig();

            $tld = $this->extractTld($domain);
            $additional = $params['additional_fields'] ?? [];

            $customerId = $this->getOrCreateCustomer($params);
            if (is_array($customerId) && ($customerId['status'] ?? '') === 'error') {
                return $customerId;
            }

            $contactId = $this->getOrCreateContact($params, $customerId);
            if (is_array($contactId) && ($contactId['status'] ?? '') === 'error') {
                return $contactId;
            }

            // Contact TLD-specific fields (WHMCS resellerclub.php:332-340 — no
            // product-key gate on transfer).
            $contactFields = $this->buildContactTldFields($tld, $params, $additional);
            if (!empty($contactFields)) {
                $this->sendCommand('set-details', 'contacts', array_merge([
                    'customer-id' => $customerId,
                    'contact-id' => $contactId,
                ], $contactFields), 'POST');
            }

            $requestData = [
                'domain-name' => $domain,
                'years' => $params['period'] ?? $params['years'] ?? 1,
                'customer-id' => $customerId,
                'reg-contact-id' => $contactId,
                'admin-contact-id' => $params['admin_contact_id'] ?? $contactId,
                'tech-contact-id' => $params['tech_contact_id'] ?? $contactId,
                'billing-contact-id' => $params['billing_contact_id'] ?? $contactId,
                'invoice-option' => 'NoInvoice',
                'purchase-privacy' => (!empty($params['id_protection']) || !empty($params['privacy'])) ? 'true' : 'false',
            ];

            $requestData = array_merge($requestData, $this->buildIdnLanguageField($additional));

            if (!empty($params['epp_code']) || !empty($params['authcode'])) {
                $requestData['auth-code'] = $params['epp_code'] ?? $params['authcode'];
            }

            $isPremium = !empty($params['is_premium']) && !empty($this->config['enable_premium_domains']);

            // .au on transfer only forwards premium (WHMCS resellerclub.php:368-375);
            // every other TLD gets the full domain field set.
            if ($tld !== 'au') {
                $requestData = array_merge(
                    $requestData,
                    $this->buildDomainTldFields($tld, $params, $additional, $contactId, $isPremium)
                );
            } elseif ($isPremium) {
                $requestData['attr-name1'] = 'premium';
                $requestData['attr-value1'] = 'true';
            }

            $result = $this->sendCommand('transfer', 'domains', $requestData, 'POST');

            if ($this->isApiError($result)) {
                return $this->errorResponse($this->getApiErrorMessage($result));
            }

            if (isset($result['actionstatus']) && $result['actionstatus'] === 'Failed') {
                return $this->errorResponse($result['actionstatusdesc'] ?? __('admin/modules.resellerclub_transfer_failed'));
            }

            Log::info('Resellercamp: Domain transfer initiated', [
                'domain' => $domain,
                'order_id' => $result['entityid'] ?? null,
            ]);

            return $this->successResponse(__('admin/modules.resellerclub_transfer_success'), [
                'domain' => $domain,
                'order_id' => $result['entityid'] ?? null,
            ]);
        } catch (\Exception $e) {
            Log::error('Resellercamp: Transfer domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Domain Renewal (WHMCS resellerclub_RenewDomain:389)
    // -------------------------------------------------------------------------

    public function renewDomain(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.resellerclub_domain_required'));
        }

        try {
            $this->validateConfig();

            $orderId = $this->getOrderId($domain);
            if (!is_numeric($orderId)) {
                return $this->errorResponse($orderId);
            }

            $details = $this->sendCommand('details', 'domains', [
                'order-id' => $orderId,
                'options' => 'OrderDetails',
            ], 'GET');

            if ($this->isApiError($details)) {
                return $this->errorResponse($this->getApiErrorMessage($details));
            }

            $expiry = $details['endtime'] ?? null;
            if (!$expiry) {
                return $this->errorResponse(__('admin/modules.resellerclub_expiry_not_found'));
            }

            $idProtection = !empty($params['id_protection']) || !empty($params['privacy']);
            $isRedemption = !empty($params['is_redemption']);

            if ($isRedemption) {
                $result = $this->sendCommand('restore', 'domains', [
                    'order-id' => $orderId,
                    'invoice-option' => 'NoInvoice',
                ], 'POST');
            } else {
                $requestData = [
                    'order-id' => $orderId,
                    'years' => $params['period'] ?? $params['years'] ?? 1,
                    'exp-date' => $expiry,
                    'invoice-option' => 'NoInvoice',
                    'purchase-privacy' => $idProtection ? 'true' : 'false',
                ];

                if (!empty($params['is_premium']) && !empty($this->config['enable_premium_domains'])) {
                    $requestData['attr-name1'] = 'premium';
                    $requestData['attr-value1'] = 'true';
                }

                $result = $this->sendCommand('renew', 'domains', $requestData, 'POST');
            }

            if ($this->isApiError($result)) {
                return $this->errorResponse($this->getApiErrorMessage($result));
            }

            // Insufficient-funds partial error (WHMCS resellerclub.php:443-445):
            // the renewal is queued but not actioned until funds are available —
            // surface it as an error rather than a silent success.
            if (is_array($result) && !empty($result['error'])) {
                return $this->errorResponse(__('admin/modules.resellerclub_renew_insufficient_funds', [
                    'error' => rtrim((string) $result['error'], '.'),
                ]));
            }

            if (isset($result['actionstatus']) && $result['actionstatus'] === 'Failed') {
                return $this->errorResponse($result['actionstatusdesc'] ?? __('admin/modules.resellerclub_renew_failed'));
            }

            // Redemption restore re-buys privacy separately (WHMCS resellerclub.php:449-451).
            if ($isRedemption && $idProtection) {
                $this->sendCommand('purchase-privacy', 'domains', [
                    'order-id' => $orderId,
                    'invoice-option' => 'NoInvoice',
                ], 'POST');
            }

            Log::info('Resellercamp: Domain renewed', ['domain' => $domain]);

            return $this->successResponse(__('admin/modules.resellerclub_renew_success'), [
                'domain' => $domain,
                'order_id' => $orderId,
            ]);
        } catch (\Exception $e) {
            Log::error('Resellercamp: Renew domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Contact Details (WHMCS resellerclub_GetContactDetails:455)
    // -------------------------------------------------------------------------

    public function getContactDetails(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.resellerclub_domain_required'));
        }

        try {
            $this->validateConfig();

            $orderId = $this->getOrderId($domain);
            if (!is_numeric($orderId)) {
                return $this->errorResponse($orderId);
            }

            $result = $this->sendCommand('details', 'domains', [
                'order-id' => $orderId,
                'options' => 'ContactIds',
            ], 'GET');

            if ($this->isApiError($result)) {
                return $this->errorResponse($this->getApiErrorMessage($result));
            }

            $contactTypes = [
                'registrantcontactid' => 'Registrant',
                'admincontactid' => 'Admin Contact',
                'techcontactid' => 'Technical Contact',
                'billingcontactid' => 'Billing Contact',
            ];

            $groups = [];
            $cachedContacts = [];

            foreach ($contactTypes as $idKey => $groupName) {
                $contactId = $result[$idKey] ?? null;
                if (!$contactId || $contactId == -1) {
                    continue;
                }

                if (!isset($cachedContacts[$contactId])) {
                    $contactResult = $this->sendCommand('details', 'contacts', [
                        'contact-id' => $contactId,
                    ], 'GET');

                    if (!$this->isApiError($contactResult)) {
                        $cachedContacts[$contactId] = $contactResult;
                    }
                }

                if (isset($cachedContacts[$contactId])) {
                    $c = $cachedContacts[$contactId];
                    $telnocc = $c['telnocc'] ?? '';
                    $telno = $c['telno'] ?? '';
                    $groups[$groupName] = [
                        'firstname' => $c['name'] ?? '',
                        'company' => $c['company'] ?? '',
                        'email' => $c['emailaddr'] ?? '',
                        // WHMCS resellerclub.php:514 — '+' . telnocc . telno.
                        'phone' => ($telnocc !== '' || $telno !== '') ? '+'.$telnocc.$telno : '',
                        'address' => $c['address1'] ?? '',
                        'address2' => $c['address2'] ?? '',
                        'city' => $c['city'] ?? '',
                        'state' => $c['state'] ?? '',
                        'postal_code' => $c['zip'] ?? '',
                        'country' => $c['country'] ?? '',
                    ];
                }
            }

            return [
                'status' => 'success',
                'message' => __('admin/modules.resellerclub_contacts_retrieved'),
                'groups' => $groups,
            ];
        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Save Contact Details (WHMCS resellerclub_SaveContactDetails:522)
    // -------------------------------------------------------------------------

    public function setContactDetails(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain) || !isset($params['groups'])) {
            return $this->errorResponse(__('admin/modules.resellerclub_domain_and_groups_required'));
        }

        try {
            $this->validateConfig();

            // .uk registration cannot be changed (WHMCS resellerclub.php:525-527).
            if ($this->extractTld($domain) === 'uk') {
                return $this->errorResponse(__('admin/modules.resellerclub_uk_contact_change_unsupported'));
            }

            $orderId = $this->getOrderId($domain);
            if (!is_numeric($orderId)) {
                return $this->errorResponse($orderId);
            }

            $details = $this->sendCommand('details', 'domains', [
                'order-id' => $orderId,
                'options' => ['ContactIds', 'OrderDetails'],
            ], 'GET');

            if ($this->isApiError($details)) {
                return $this->errorResponse($this->getApiErrorMessage($details));
            }

            $customerId = $details['customerid'] ?? null;
            if (!$customerId) {
                return $this->errorResponse(__('admin/modules.resellerclub_customer_not_found'));
            }

            // Current on-record contacts (for change detection).
            $existing = $this->getContactDetails(['domain' => $domain]);
            $existingGroups = ($existing['groups'] ?? []);

            $contactIdMap = [
                'Registrant' => $details['registrantcontactid'] ?? 0,
                'Admin Contact' => $details['admincontactid'] ?? 0,
                'Technical Contact' => $details['techcontactid'] ?? 0,
                'Billing Contact' => $details['billingcontactid'] ?? 0,
            ];
            $paramKeyMap = [
                'Registrant' => 'reg-contact-id',
                'Admin Contact' => 'admin-contact-id',
                'Technical Contact' => 'tech-contact-id',
                'Billing Contact' => 'billing-contact-id',
            ];
            $canonicalGroup = [
                'Registrant' => 'registrant',
                'Admin Contact' => 'admin',
                'Technical Contact' => 'tech',
                'Billing Contact' => 'billing',
            ];

            $newContactIds = $contactIdMap;
            $changed = false;
            $registrantFieldsChanged = false;

            foreach ($contactIdMap as $groupName => $currentContactId) {
                if ((int) $currentContactId <= 0 || !isset($params['groups'][$groupName])) {
                    continue;
                }

                // Read the submitted contact through the PRC-969 canonical helper
                // so `address1`/`postcode`/etc. line up regardless of submitter key.
                $canonical = $this->contact([$canonicalGroup[$groupName] => $params['groups'][$groupName]], $canonicalGroup[$groupName]);

                if (!$this->contactGroupChanged($canonical, $existingGroups[$groupName] ?? [], $groupName, $registrantFieldsChanged)) {
                    continue;
                }

                $changed = true;

                [$phoneCc, $phone] = $this->splitPhoneCc($canonical['phone'] ?? '');

                $contactData = [
                    'customer-id' => $customerId,
                    'email' => $canonical['email'] ?? '',
                    'name' => trim(($canonical['first_name'] ?? '').' '.($canonical['last_name'] ?? '')) ?: 'Domain Owner',
                    'company' => ($canonical['company'] ?? '') !== '' ? $canonical['company'] : 'N/A',
                    'address-line-1' => substr($canonical['address1'] ?? '', 0, 64),
                    'address-line-2' => substr($canonical['address2'] ?? '', 0, 64),
                    'city' => $canonical['city'] ?? '',
                    'state' => $canonical['state'] ?? '',
                    'zipcode' => $canonical['postcode'] ?? '',
                    'country' => $canonical['country_code'] ?? '',
                    'phone-cc' => $phoneCc,
                    'phone' => $phone,
                    'type' => 'Contact',
                ];

                $contactResult = $this->sendCommand('add', 'contacts', $contactData, 'POST');
                if ($this->isApiError($contactResult)) {
                    return $this->errorResponse($this->getApiErrorMessage($contactResult));
                }

                $newContactIds[$groupName] = is_numeric($contactResult)
                    ? $contactResult
                    : ($contactResult['contactid'] ?? $currentContactId);
            }

            $values = ['status' => 'success', 'message' => __('admin/modules.resellerclub_contacts_updated'), 'data' => ['domain' => $domain]];

            if ($changed) {
                $updateData = ['order-id' => $orderId];
                foreach ($paramKeyMap as $groupName => $paramKey) {
                    $updateData[$paramKey] = $newContactIds[$groupName];
                }

                // IRTP: designated-agent / sixty-day-lock-optout only when the
                // registrant name/company/email changed (WHMCS resellerclub.php:621-628).
                if ($registrantFieldsChanged) {
                    if (!empty($params['designated_agent'])) {
                        $updateData['designated-agent'] = 'true';
                    } elseif (array_key_exists('irtp_opt_out', $params)) {
                        $updateData['sixty-day-lock-optout'] = !empty($params['irtp_opt_out']) ? 'true' : 'false';
                    }
                }

                $result = $this->sendCommand('modify-contact', 'domains', $updateData, 'POST');
                if ($this->isApiError($result)) {
                    return $this->errorResponse($this->getApiErrorMessage($result));
                }
            }

            return $values;
        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Domain Details (WHMCS resellerclub_GetDomainInformation:2485)
    // -------------------------------------------------------------------------

    public function getDomainDetails(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.resellerclub_domain_required'));
        }

        try {
            $this->validateConfig();

            $orderId = $this->getOrderId($domain);
            if (!is_numeric($orderId)) {
                return $this->errorResponse($orderId);
            }

            $result = $this->sendCommand('details', 'domains', [
                'order-id' => $orderId,
                'options' => 'All',
            ], 'GET');

            if ($this->isApiError($result)) {
                return $this->errorResponse($this->getApiErrorMessage($result));
            }

            // WHMCS resellerclub.php:2511-2513 — iterate up to noOfNameServers.
            $nameservers = [];
            $nsCount = (int) ($result['noOfNameServers'] ?? 0);
            $max = $nsCount > 0 ? $nsCount : 5;
            for ($i = 1; $i <= $max; $i++) {
                if (!empty($result['ns'.$i])) {
                    $nameservers[] = $result['ns'.$i];
                }
            }

            $expiresAt = isset($result['endtime']) ? date('Y-m-d H:i:s', $result['endtime']) : null;
            $createdAt = isset($result['creationtime']) ? date('Y-m-d H:i:s', $result['creationtime']) : null;

            // WHMCS resellerclub.php:2516 — transfer lock is a membership test on
            // the orderstatus[] array; :2515 — sixtydaylock on domainstatus[].
            $orderStatus = (array) ($result['orderstatus'] ?? []);
            $domainStatus = (array) ($result['domainstatus'] ?? []);
            $locked = in_array('transferlock', $orderStatus, true);
            $irtpLock = in_array('sixtydaylock', $domainStatus, true);

            // LogicBoxes does not expose an auto-renew flag in domain details;
            // only surface it when a real recurring field is present.
            $autoRenew = null;
            if (array_key_exists('recurring', $result)) {
                $autoRenew = filter_var($result['recurring'], FILTER_VALIDATE_BOOLEAN);
            } elseif (array_key_exists('autoRenewal', $result)) {
                $autoRenew = filter_var($result['autoRenewal'], FILTER_VALIDATE_BOOLEAN);
            }

            return $this->successResponse(__('admin/modules.resellerclub_domain_details_retrieved'), [
                'domain' => $domain,
                'order_id' => $orderId,
                'status' => $result['currentstatus'] ?? 'Unknown',
                'locked' => $locked,
                'irtp_lock' => $irtpLock,
                // isprivacyprotected is the string "false" when off (WHMCS :2536).
                'privacy_enabled' => isset($result['isprivacyprotected'])
                    && $result['isprivacyprotected'] !== 'false'
                    && !empty($result['isprivacyprotected']),
                'auto_renew' => $autoRenew,
                'created_at' => $createdAt,
                'expires_at' => $expiresAt,
                'nameservers' => $nameservers,
                'customer_id' => $result['customerid'] ?? null,
                'epp_code' => $result['domsecret'] ?? null,
            ]);
        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Domain Availability (WHMCS resellerclub_CheckAvailability:2280)
    // -------------------------------------------------------------------------

    public function checkDomainAvailability(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.resellerclub_domain_required'));
        }

        try {
            $this->validateConfig();

            $parts = explode('.', $domain, 2);
            $sld = $parts[0] ?? '';
            $tld = $parts[1] ?? '';

            if (empty($sld) || empty($tld)) {
                return $this->errorResponse(__('admin/modules.resellerclub_invalid_domain'));
            }

            // LogicBoxes expects bare TLDs ("com"), but callers pass dotted TLDs
            // per the visibleTlds() convention (".com"). Strip the leading dot on
            // every entry (WHMCS resellerclub.php:2287-2289: substr($value, 1)).
            $tlds = $params['tlds'] ?? [$tld];
            $tlds = array_map(fn ($t) => ltrim((string) $t, '.'), $tlds);
            $tld = ltrim($tld, '.');

            $originalEndpoint = $this->apiEndpoint;
            $this->apiEndpoint = $this->availabilityEndpoint;

            $result = $this->sendCommand('available', 'domains', [
                'domain-name' => $sld,
                'tlds' => $tlds,
            ], 'GET');

            $this->apiEndpoint = $originalEndpoint;

            if ($this->isApiError($result)) {
                return $this->errorResponse($this->getApiErrorMessage($result));
            }

            $domainKey = $sld.'.'.$tld;
            $available = false;
            $status = 'unknown';

            if (isset($result[$domainKey])) {
                $domainData = $result[$domainKey];
                $apiStatus = $domainData['status'] ?? '';
                if ($apiStatus === 'available') {
                    $available = true;
                    $status = 'available';
                } elseif ($apiStatus === 'regthroughus') {
                    $status = 'registered_through_us';
                } elseif ($apiStatus === 'regthroughothers') {
                    $status = 'registered';
                } else {
                    $status = $apiStatus ?: 'unknown';
                }
            }

            return [
                'status' => 'success',
                'message' => __('admin/modules.resellerclub_availability_checked'),
                'data' => [
                    'domain' => $domain,
                    'available' => $available,
                    'domain_status' => $status,
                    'registrar' => 'resellercamp',
                ],
                'success' => true,
                'available' => $available,
                'domain' => $domain,
                'registrar' => 'resellercamp',
            ];
        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    // =========================================================================
    //  TLD-specific field builders (WHMCS ContactTLDSpecificFields:1407 +
    //  DomainTLDSpecificFields:1587). Pure — no network.
    // =========================================================================

    /**
     * Extract the last TLD segment from the domain string (PRC-49: never from
     * tld_id / DB). e.g. "example.co.uk" → "uk".
     */
    protected function extractTld(string $domain): string
    {
        $parts = explode('.', strtolower(trim($domain)));

        return end($parts) ?: '';
    }

    /**
     * idnLanguageCode attr (WHMCS resellerclub.php:229-234).
     *
     * @return array<string,string>
     */
    protected function buildIdnLanguageField(array $additional): array
    {
        $raw = $additional['IDN Language'] ?? $additional['idnLanguageCode'] ?? '';
        $idnLang = explode('|', (string) $raw)[0] ?? '';

        if ($idnLang !== '' && $idnLang !== 'NOIDN') {
            return ['attr-name1' => 'idnLanguageCode', 'attr-value1' => $idnLang];
        }

        return [];
    }

    /**
     * Contact-level TLD attributes (.us/.ca/.es/.asia/.ru/.pro/.nl/.tel/.uk),
     * pushed via set-details/contacts. WHMCS resellerclub_ContactTLDSpecificFields:1407.
     *
     * @return array<string,string>
     */
    /** Reformat a Y-m-d date to the .ru dd.mm.yyyy form (WHMCS:1546). */
    protected function ruDate(string $date): string
    {
        $p = explode('-', trim($date));

        return isset($p[2]) ? $p[2].'.'.$p[1].'.'.$p[0] : trim($date);
    }

    protected function buildContactTldFields(string $tld, array $params, array $additional): array
    {
        $c = $this->contact($params, 'registrant');
        $fields = [];

        switch ($tld) {
            case 'us':
                $purposeMap = [
                    'Business use for profit' => 'P1',
                    'Non-profit business' => 'P2',
                    'Club' => 'P2',
                    'Association' => 'P2',
                    'Religious Organization' => 'P2',
                    'Personal Use' => 'P3',
                    'Educational purposes' => 'P4',
                    'Government purposes' => 'P5',
                ];
                $purpose = $additional['Application Purpose'] ?? '';
                $fields['attr-name1'] = 'purpose';
                $fields['attr-value1'] = (string) ($purposeMap[$purpose] ?? $purpose);
                $fields['attr-name2'] = 'category';
                $fields['attr-value2'] = (string) ($additional['Nexus Category'] ?? '');
                $fields['product-key'] = 'domus';
                break;

            case 'uk':
                if (!empty($additional['Registrant Name'])) {
                    $fields['name'] = $additional['Registrant Name'];
                }
                break;

            case 'ca':
                $legalMap = [
                    'Corporation' => 'CCO',
                    'Canadian Citizen' => 'CCT',
                    'Permanent Resident of Canada' => 'RES',
                    'Government' => 'GOV',
                    'Canadian Educational Institution' => 'EDU',
                    'Canadian Unincorporated Association' => 'ASS',
                    'Canadian Hospital' => 'HOP',
                    'Partnership Registered in Canada' => 'PRT',
                    'Trade-mark registered in Canada' => 'TDM',
                ];
                $legal = $additional['Legal Type'] ?? '';
                $fields['attr-name1'] = 'CPR';
                $fields['attr-value1'] = (string) ($legalMap[$legal] ?? 'CCO');
                $fields['attr-name2'] = 'AgreementVersion';
                $fields['attr-value2'] = '2.0';
                $fields['attr-name3'] = 'AgreementValue';
                $fields['attr-value3'] = 'y';
                $fields['product-key'] = 'dotca';
                break;

            case 'es':
                $legalType = explode('|', (string) ($additional['Legal Entity Type'] ?? ''))[0] ?: '1';
                if ($legalType === '1') {
                    $fields['company'] = 'N/A';
                }
                $idType = explode('|', (string) ($additional['ID Form Type'] ?? ''))[0] ?? '';
                $idMap = [
                    'Other Identification' => '0',
                    'Tax Identification Number' => '1',
                    'Tax Identification Code' => '1',
                    'Foreigner Identification Number' => '3',
                ];
                $fields['attr-name1'] = 'es_form_juridica';
                $fields['attr-value1'] = (string) $legalType;
                $fields['attr-name2'] = 'es_tipo_identificacion';
                $fields['attr-value2'] = (string) ($idMap[$idType] ?? $idType);
                $fields['attr-name3'] = 'es_identificacion';
                $fields['attr-value3'] = (string) ($additional['ID Form Number'] ?? '');
                $fields['product-key'] = 'dotes';
                break;

            case 'ru':
                // .ru registrant eligibility block (WHMCS resellerclub.php:1532-1555).
                $fields['attr-name1'] = 'contract-type';
                if (($additional['Registrant Type'] ?? '') === 'ORG') {
                    $fields['attr-value1'] = 'ORG';
                    $fields['attr-name3'] = 'org-r';
                    $fields['attr-value3'] = (string) ($c['company'] ?? '');
                    $fields['attr-name6'] = 'kpp';
                    $fields['attr-value6'] = (string) ($additional['Russian Organizations Territory-Linked Taxpayer Number 2'] ?? '');
                    $fields['attr-name7'] = 'code';
                    $fields['attr-value7'] = (string) ($additional['Russian Organizations Taxpayer Number 1'] ?? '');
                } else {
                    $fields['attr-value1'] = 'PRS';
                    $fields['attr-name2'] = 'birth-date';
                    $fields['attr-value2'] = $this->ruDate($additional['Individuals Birthday'] ?? '');
                    $fields['attr-name4'] = 'person-r';
                    $fields['attr-value4'] = trim(($c['first_name'] ?? '').' '.($c['last_name'] ?? ''));
                    $fields['attr-name8'] = 'passport';
                    $fields['attr-value8'] = (string) ($additional['Individuals Passport Number'] ?? '')
                        .', issued by '.(string) ($additional['Individuals Passport Issuer'] ?? '')
                        .', '.$this->ruDate($additional['Individuals Passport Issue Date'] ?? '');
                }
                $fields['attr-name5'] = 'address-r';
                $fields['attr-value5'] = trim(($c['address1'] ?? '').' '.($c['city'] ?? '').' '.($c['state'] ?? '').' '.($c['country_code'] ?? '').' '.($c['postcode'] ?? ''));
                break;

            case 'asia':
                $fields['attr-name1'] = 'locality';
                $fields['attr-value1'] = (string) ($c['country_code'] ?? '');
                $fields['attr-name2'] = 'legalentitytype';
                $fields['attr-value2'] = (string) ($additional['Legal Type'] ?? '');
                $fields['attr-name3'] = 'identform';
                $fields['attr-value3'] = (string) ($additional['Identity Form'] ?? '');
                $fields['attr-name4'] = 'identnumber';
                $fields['attr-value4'] = (string) ($additional['Identity Number'] ?? '');
                $fields['product-key'] = 'dotasia';
                break;

            case 'pro':
                $fields['attr-name1'] = 'profession';
                $fields['attr-value1'] = (string) ($additional['Profession'] ?? '');
                $fields['product-key'] = 'dotpro';
                break;

            case 'nl':
                $fields['attr-name1'] = 'legalForm';
                $fields['attr-value1'] = ($c['company'] ?? '') !== '' ? 'ANDERS' : 'PERSOON';
                $fields['product-key'] = 'dotnl';
                break;

            case 'tel':
                $legal = $additional['Legal Type'] ?? '';
                $fields['attr-name1'] = 'whois-type';
                $fields['attr-value1'] = ($legal === 'Legal Person') ? 'Legal' : 'Natural';
                break;
        }

        return $fields;
    }

    /**
     * Domain-level TLD attributes + -1 contact overrides + premium
     * (WHMCS resellerclub_DomainTLDSpecificFields:1587).
     *
     * @return array<string,string>
     */
    protected function buildDomainTldFields(string $tld, array $params, array $additional, $contactId, bool $isPremium): array
    {
        $fields = [];
        $fieldCount = 0;

        if (in_array($tld, ['es', 'de'], true)) {
            $fields['purchase-privacy'] = '0';
        } elseif ($tld === 'asia') {
            $fields['attr-name1'] = 'cedcontactid';
            $fields['attr-value1'] = (string) $contactId;
            $fieldCount = 1;
        } elseif (in_array($tld, ['uk', 'eu', 'nz', 'ru'], true)) {
            $fields['admin-contact-id'] = '-1';
            $fields['tech-contact-id'] = '-1';
            $fields['billing-contact-id'] = '-1';
        } elseif (in_array($tld, ['ca', 'nl', 'london'], true)) {
            $fields['billing-contact-id'] = '-1';
        } elseif ($tld === 'cn') {
            $fields['attr-name1'] = 'cnhosting';
            $fields['attr-value1'] = !empty($additional['cnhosting']) ? 'true' : 'false';
            $fields['attr-name2'] = 'cnhostingclause';
            $fields['attr-value2'] = !empty($additional['cnhregisterclause']) ? 'yes' : 'no';
            $fieldCount = 2;
        } elseif ($tld === 'au') {
            $fields = $this->buildAuFields($additional);
            $fieldCount = 7;
        } elseif ($tld === 'tel') {
            $fields['attr-name2'] = 'publish';
            $fields['attr-value2'] = !empty($additional['WHOIS Opt-out']) ? 'Y' : 'N';
            $fieldCount = 2;
        }

        if ($isPremium) {
            $fields['attr-name'.($fieldCount + 1)] = 'premium';
            $fields['attr-value'.($fieldCount + 1)] = 'true';
        }

        return $fields;
    }

    /**
     * .au eligibility attrs (WHMCS resellerclub.php:1615-1689).
     *
     * @return array<string,string>
     */
    protected function buildAuFields(array $additional): array
    {
        $idTypeMap = [
            'Australian Company Number (ACN)' => 'ACN',
            'ACT Business Number' => 'ACT BN',
            'NSW Business Number' => 'NSW BN',
            'NT Business Number' => 'NT BN',
            'QLD Business Number' => 'QLD BN',
            'SA Business Number' => 'SA BN',
            'TAS Business Number' => 'TAS BN',
            'VIC Business Number' => 'VIC BN',
            'WA Business Number' => 'WA BN',
            'Trademark (TM)' => 'TM',
            'Australian Business Number (ABN)' => 'ABN',
            'Australian Registered Body Number (ARBN)' => 'ARBN',
            'Other - Used to record an Incorporated Association number' => 'Other',
        ];

        $val5 = $idTypeMap[$additional['Eligibility ID Type'] ?? ''] ?? 'ABN';
        $val4 = $additional['Eligibility ID'] ?? '';

        $fields = [
            'attr-name1' => 'id-type',
            'attr-name2' => 'id',
            'attr-name3' => 'policyReason',
            'attr-name4' => 'isAUWarranty',
            'attr-value4' => 'true',
        ];

        if (($additional['Eligibility Reason'] ?? '') === 'Domain name is an Exact Match Abbreviation or Acronym of your Entity or Trading Name.') {
            $fields['attr-value3'] = '1';
        } else {
            $fields['attr-value3'] = '2';
            $fields['attr-value4'] = 'true';
        }

        $fields['attr-value1'] = $val5;
        $fields['attr-value2'] = $val4;

        if ($val5 === 'TM' || $val5 === 'Other') {
            $fields['attr-name5'] = 'eligibilityType';
            $fields['attr-value5'] = $val5 === 'Other' ? 'Other' : 'Trademark Owner';
        }

        if ($val5 === 'TM') {
            $fields['attr-name6'] = 'eligibilityName';
            $fields['attr-value6'] = $additional['Eligibility Name'] ?? '';
        }

        $regNameVals = ['VIC BN', 'NSW BN', 'SA BN', 'NT BN', 'WA BN', 'TAS BN', 'ACT BN', 'QLD BN', 'TM', 'Other'];
        if (in_array($val5, $regNameVals, true)) {
            $fields['attr-name7'] = 'registrantName';
            $fields['attr-value7'] = $additional['Registrant Name'] ?? '';
        }

        return $fields;
    }

    /**
     * Whether ANY of the tracked fields changed for a contact group; sets
     * $registrantFieldsChanged when a registrant name/company/email changed
     * (IRTP trigger). WHMCS resellerclub.php:563-570.
     */
    protected function contactGroupChanged(array $canonical, array $existing, string $groupName, bool &$registrantFieldsChanged): bool
    {
        // Compare canonical(new) → existing (existing shape from getContactDetails).
        $compare = [
            'name' => trim(($canonical['first_name'] ?? '').' '.($canonical['last_name'] ?? '')),
            'company' => ($canonical['company'] ?? '') !== '' ? $canonical['company'] : 'N/A',
            'email' => $canonical['email'] ?? '',
            'address' => $canonical['address1'] ?? '',
            'address2' => $canonical['address2'] ?? '',
            'city' => $canonical['city'] ?? '',
            'state' => $canonical['state'] ?? '',
            'postal_code' => $canonical['postcode'] ?? '',
            'country' => $canonical['country_code'] ?? '',
        ];

        $existingName = $existing['firstname'] ?? '';
        $existingCompany = ($existing['company'] ?? '') !== '' ? $existing['company'] : 'N/A';

        $changed = $compare['name'] !== $existingName
            || $compare['company'] !== $existingCompany
            || $compare['email'] !== ($existing['email'] ?? '')
            || $compare['address'] !== ($existing['address'] ?? '')
            || $compare['address2'] !== ($existing['address2'] ?? '')
            || $compare['city'] !== ($existing['city'] ?? '')
            || $compare['state'] !== ($existing['state'] ?? '')
            || $compare['postal_code'] !== ($existing['postal_code'] ?? '')
            || $compare['country'] !== ($existing['country'] ?? '');

        if ($groupName === 'Registrant'
            && ($compare['name'] !== $existingName
                || $compare['company'] !== $existingCompany
                || $compare['email'] !== ($existing['email'] ?? ''))) {
            $registrantFieldsChanged = true;
        }

        return $changed;
    }

    /**
     * Validate an Australian Business Number checksum
     * (WHMCS resellerclub_validateABN).
     */
    protected function validateAbn($abn): bool
    {
        $abn = preg_replace('/[^0-9]/', '', (string) $abn);
        if (strlen($abn) !== 11) {
            return false;
        }

        $weights = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
        $sum = 0;
        foreach (str_split($abn) as $i => $digit) {
            $d = (int) $digit;
            if ($i === 0) {
                $d -= 1;
            }
            $sum += $d * $weights[$i];
        }

        return $sum % 89 === 0;
    }
}
