<?php

namespace App\Modules\Registrar\Godaddy;

use App\Core\Registrar\AbstractRegistrar;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class Godaddy extends AbstractRegistrar
{
    /**
     * GoDaddy API live base URL
     */
    protected const API_URL_LIVE = 'https://api.godaddy.com/v1/';

    /**
     * GoDaddy API test (OTE) base URL
     */
    protected const API_URL_TEST = 'https://api.ote-godaddy.com/v1/';

    /**
     * Get the API base URL based on test mode setting
     */
    protected function getApiUrl(): string
    {
        $testMode = $this->config['test_mode'] ?? false;

        return $testMode ? self::API_URL_TEST : self::API_URL_LIVE;
    }

    /**
     * Get the authorization header value
     */
    protected function getAuthHeader(): string
    {
        $apiKey = $this->config['api_key'] ?? '';
        $apiSecret = $this->config['api_secret'] ?? '';

        return "sso-key {$apiKey}:{$apiSecret}";
    }

    /**
     * Send an API request to GoDaddy
     *
     * @param  string  $endpoint  API endpoint (relative to base URL)
     * @param  string  $method  HTTP method (GET, POST, PUT, PATCH, DELETE)
     * @param  array  $data  Request body data
     * @param  array  $query  Query parameters
     * @return array Parsed JSON response
     *
     * @throws \Exception On HTTP or API error
     */
    protected function apiRequest(string $endpoint, string $method = 'GET', array $data = [], array $query = []): array
    {
        $url = $this->getApiUrl() . ltrim($endpoint, '/');

        $headers = [
            'Authorization' => $this->getAuthHeader(),
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ];

        $request = Http::withHeaders($headers)->timeout(30);

        if (! empty($query)) {
            $request = $request->withQueryParameters($query);
        }

        switch (strtoupper($method)) {
            case 'POST':
                $response = $request->post($url, $data);
                break;
            case 'PUT':
                $response = $request->put($url, $data);
                break;
            case 'PATCH':
                $response = $request->patch($url, $data);
                break;
            case 'DELETE':
                $response = $request->delete($url, $data);
                break;
            case 'GET':
            default:
                $response = $request->get($url);
                break;
        }

        $statusCode = $response->status();
        $body = $response->json() ?? [];

        Log::channel('daily')->debug('GoDaddy API request', [
            'endpoint' => $endpoint,
            'method' => $method,
            'status' => $statusCode,
        ]);

        if ($response->failed()) {
            $errorMessage = $body['message'] ?? ($body['error'] ?? __('admin/modules.godaddy_api_error'));

            Log::channel('daily')->error('GoDaddy API error', [
                'endpoint' => $endpoint,
                'method' => $method,
                'status' => $statusCode,
                'error' => $errorMessage,
                'body' => $body,
            ]);

            throw new \Exception($errorMessage);
        }

        return $body;
    }

    // -------------------------------------------------------------------------
    // Configuration
    // -------------------------------------------------------------------------

    /**
     * Return module configuration fields
     */
    public function getConfigFields(): array
    {
        return [
            'api_key' => [
                'type' => 'text',
                'label' => __('admin/modules.godaddy_field_api_key_label'),
                'required' => true,
                'description' => __('admin/modules.godaddy_field_api_key_description'),
                'placeholder' => 'your-api-key',
                'value' => $this->config['api_key'] ?? '',
            ],
            'api_secret' => [
                'type' => 'password',
                'label' => __('admin/modules.godaddy_field_api_secret_label'),
                'required' => true,
                'description' => __('admin/modules.godaddy_field_api_secret_description'),
                'placeholder' => '********',
                'value' => $this->config['api_secret'] ?? '',
            ],
            'test_mode' => [
                'type' => 'checkbox',
                'label' => __('admin/modules.godaddy_field_test_mode_label'),
                'required' => false,
                'description' => __('admin/modules.godaddy_field_test_mode_description'),
                'default' => true,
                'value' => $this->config['test_mode'] ?? true,
            ],
        ];
    }

    /**
     * Validate module configuration
     */
    public function validateConfig(): array
    {
        $errors = [];

        if (empty($this->config['api_key'])) {
            $errors[] = __('admin/modules.godaddy_validation_api_key_required');
        }

        if (empty($this->config['api_secret'])) {
            $errors[] = __('admin/modules.godaddy_validation_api_secret_required');
        }

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

        // Attempt a test API call to verify credentials
        try {
            $this->apiRequest('domains/available', 'GET', [], ['domain' => 'example.com']);
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_validation_connection_failed'),
                'data' => ['errors' => [$e->getMessage()]],
            ];
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.godaddy_validation_success'),
        ];
    }

    /**
     * Return module info
     */
    public function getModuleInfo(): array
    {
        return array_merge(parent::getModuleInfo(), [
            'name' => 'godaddy',
            'label' => 'GoDaddy',
            'description' => __('admin/modules.godaddy_module_description'),
        ]);
    }

    // -------------------------------------------------------------------------
    // Domain Registration
    // -------------------------------------------------------------------------

    /**
     * Register a new domain
     *
     * Expected params:
     *  - domain (string): Full domain name (e.g. example.com)
     *  - period (int): Registration period in years
     *  - nameservers (array, optional): List of nameservers
     *  - contacts (array, optional): Contact details keyed by type
     *  - privacy (bool, optional): Enable WHOIS privacy
     *  - additional_fields (array, optional): TLD-specific fields
     */
    public function registerDomain(array $params)
    {
        $requiredParams = ['domain', 'period'];
        foreach ($requiredParams as $param) {
            if (empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.godaddy_missing_param', ['param' => $param]),
                ];
            }
        }

        $domain = $params['domain'];
        $period = (int) $params['period'];

        // Build contacts payload (PRC-969 canonical groups → GoDaddy shape)
        $contacts = $this->buildProvisioningContacts($params);

        // Build nameservers
        $nameservers = $params['nameservers'] ?? [];

        // Build agreement consent
        $tld = $this->extractTld($domain);
        $privacy = $params['privacy'] ?? false;

        try {
            $agreementKeys = $this->getAgreementKeys($tld, $privacy, false);
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_agreement_error', ['error' => $e->getMessage()]),
            ];
        }

        // TLD-specific extra fields (.us/.ca/.es/.eu/.nyc/.sg …). WHMCS forwards
        // godaddy_additional_fields($params) into the purchase body (godaddy.php:57).
        $extraFields = $this->mapAdditionalFields($tld, $params['additional_fields'] ?? []);

        $body = array_merge([
            'domain' => $domain,
            'period' => $period,
            'privacy' => $privacy,
            'renewAuto' => $params['auto_renew'] ?? false,
            'nameServers' => $nameservers,
            'consent' => [
                'agreedAt' => now()->toIso8601String(),
                'agreedBy' => request()->ip() ?? '127.0.0.1',
                'agreementKeys' => $agreementKeys,
            ],
        ], $contacts, $extraFields);

        try {
            $response = $this->apiRequest('domains/purchase', 'POST', $body);

            Log::info('GoDaddy: Domain registered', [
                'action' => 'registered',
                'domain' => $domain,
                'period' => $period,
            ]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_register_success'),
                'data' => [
                    'domain' => $domain,
                    'period' => $period,
                    'order_id' => $response['orderId'] ?? null,
                    'registered_at' => now()->toISOString(),
                    // Canonical date-key alias (kept alongside registered_at).
                    'registration_date' => now()->toISOString(),
                    'expires_at' => now()->addYears($period)->toISOString(),
                    'nameservers' => $nameservers,
                ],
            ];
        } catch (\Exception $e) {
            Log::error('GoDaddy: Domain registration failed', [
                'domain' => $domain,
                'error' => $e->getMessage(),
            ]);

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

    // -------------------------------------------------------------------------
    // Domain Transfer
    // -------------------------------------------------------------------------

    /**
     * Transfer a domain to GoDaddy
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - epp_code (string): Authorization/EPP code
     *  - period (int, optional): Transfer period in years (default 1)
     *  - nameservers (array, optional): Nameservers
     *  - contacts (array, optional): Contact details
     *  - privacy (bool, optional): Enable WHOIS privacy
     */
    public function transferDomain(array $params)
    {
        $requiredParams = ['domain', 'epp_code'];
        foreach ($requiredParams as $param) {
            if (empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.godaddy_missing_param', ['param' => $param]),
                ];
            }
        }

        $domain = $params['domain'];
        $period = (int) ($params['period'] ?? 1);
        $privacy = $params['privacy'] ?? false;
        $tld = $this->extractTld($domain);

        // Build contacts payload (PRC-969 canonical groups → GoDaddy shape)
        $contacts = $this->buildProvisioningContacts($params);

        // Build nameservers
        $nameservers = $params['nameservers'] ?? [];

        try {
            $agreementKeys = $this->getAgreementKeys($tld, $privacy, true);
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_agreement_error', ['error' => $e->getMessage()]),
            ];
        }

        // TLD-specific extra fields — WHMCS forwards godaddy_additional_fields($params)
        // into the transfer body too (godaddy.php:124).
        $extraFields = $this->mapAdditionalFields($tld, $params['additional_fields'] ?? []);

        $body = array_merge([
            'authCode' => $params['epp_code'],
            'domain' => $domain,
            'period' => $period,
            'privacy' => $privacy,
            'renewAuto' => false,
            'nameServers' => $nameservers,
            'consent' => [
                'agreedAt' => now()->toIso8601String(),
                'agreedBy' => request()->ip() ?? '127.0.0.1',
                'agreementKeys' => $agreementKeys,
            ],
        ], $contacts, $extraFields);

        try {
            $response = $this->apiRequest("domains/{$domain}/transfer", 'POST', $body);

            Log::info('GoDaddy: Domain transfer initiated', [
                'action' => 'transfer',
                'domain' => $domain,
            ]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_transfer_success'),
                'data' => [
                    'domain' => $domain,
                    'period' => $period,
                    'order_id' => $response['orderId'] ?? null,
                    'transfer_id' => $response['orderId'] ?? null,
                    'transfer_status' => 'pending',
                    'transfer_started_at' => now()->toISOString(),
                    'estimated_completion' => now()->addDays(5)->toISOString(),
                    'expires_at' => now()->addYears($period)->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            Log::error('GoDaddy: Domain transfer failed', [
                'domain' => $domain,
                'error' => $e->getMessage(),
            ]);

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

    // -------------------------------------------------------------------------
    // Domain Renewal
    // -------------------------------------------------------------------------

    /**
     * Renew a domain
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - period (int): Renewal period in years
     */
    public function renewDomain(array $params)
    {
        $requiredParams = ['domain', 'period'];
        foreach ($requiredParams as $param) {
            if (empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.godaddy_missing_param', ['param' => $param]),
                ];
            }
        }

        $domain = $params['domain'];
        $period = (int) $params['period'];

        try {
            $response = $this->apiRequest("domains/{$domain}/renew", 'POST', [
                'period' => $period,
            ]);

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

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_renew_success'),
                'data' => [
                    'domain' => $domain,
                    'period' => $period,
                    'renewed_at' => now()->toISOString(),
                    // Canonical date-key alias (kept alongside renewed_at).
                    'renewal_date' => now()->toISOString(),
                    'expires_at' => $response['expirationDate'] ?? now()->addYears($period)->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            Log::error('GoDaddy: Domain renewal failed', [
                'domain' => $domain,
                'error' => $e->getMessage(),
            ]);

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

    // -------------------------------------------------------------------------
    // Nameservers
    // -------------------------------------------------------------------------

    /**
     * Get nameservers for a domain
     *
     * Expected params:
     *  - domain (string): Full domain name
     */
    public function getNameservers(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest("domains/{$domain}");

            $nameservers = $response['nameServers'] ?? [];

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_get_nameservers_success'),
                'data' => [
                    'domain' => $domain,
                    'nameservers' => $nameservers,
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Set nameservers for a domain
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - nameservers (array): List of nameserver hostnames
     */
    public function setNameservers(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        if (empty($params['nameservers']) || ! is_array($params['nameservers'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'nameservers']),
            ];
        }

        $domain = $params['domain'];

        try {
            $this->apiRequest("domains/{$domain}", 'PATCH', [
                'nameServers' => array_values($params['nameservers']),
            ]);

            Log::info('GoDaddy: Nameservers updated', [
                'domain' => $domain,
                'nameservers' => $params['nameservers'],
            ]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_set_nameservers_success'),
                'data' => [
                    'domain' => $domain,
                    'nameservers' => $params['nameservers'],
                    'updated_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    // -------------------------------------------------------------------------
    // Contact Details
    // -------------------------------------------------------------------------

    /**
     * Get contact details for a domain
     *
     * Expected params:
     *  - domain (string): Full domain name
     *
     * Returns groups: Registrant, Admin, Tech
     */
    public function getContactDetails(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest("domains/{$domain}");

            $contactTypes = ['Registrant', 'Admin', 'Tech'];
            $groups = [];

            foreach ($contactTypes as $contactType) {
                $key = 'contact' . $contactType;
                if (isset($response[$key])) {
                    $contact = $response[$key];
                    $address = $contact['addressMailing'] ?? [];

                    $groups[$contactType] = [
                        'firstname' => $contact['nameFirst'] ?? '',
                        'lastname' => $contact['nameLast'] ?? '',
                        'company' => $contact['organization'] ?? '',
                        'email' => $contact['email'] ?? '',
                        'phone' => $contact['phone'] ?? '',
                        'fax' => $contact['fax'] ?? '',
                        'job_title' => $contact['jobTitle'] ?? '',
                        'address' => $address['address1'] ?? '',
                        'address2' => $address['address2'] ?? '',
                        'city' => $address['city'] ?? '',
                        'state' => $address['state'] ?? '',
                        'postal_code' => $address['postalCode'] ?? '',
                        'country' => $address['country'] ?? '',
                    ];
                }
            }

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

    /**
     * Set contact details for a domain
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - groups (array): Contact groups keyed by type (Registrant, Admin, Tech)
     */
    public function setContactDetails(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        if (empty($params['groups']) || ! is_array($params['groups'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'groups']),
            ];
        }

        $domain = $params['domain'];
        $groups = $params['groups'];

        $contacts = [];
        $contactTypes = ['Registrant', 'Admin', 'Tech'];

        foreach ($contactTypes as $contactType) {
            if (isset($groups[$contactType])) {
                $g = $groups[$contactType];
                $contacts['contact' . $contactType] = [
                    'addressMailing' => [
                        'address1' => $g['address'] ?? '',
                        'address2' => $g['address2'] ?? '',
                        'city' => $g['city'] ?? '',
                        'country' => $g['country'] ?? '',
                        'postalCode' => $g['postal_code'] ?? '',
                        'state' => $g['state'] ?? '',
                    ],
                    'email' => $g['email'] ?? '',
                    'jobTitle' => $g['job_title'] ?? '',
                    'nameFirst' => $g['firstname'] ?? '',
                    'nameLast' => $g['lastname'] ?? '',
                    'organization' => $g['company'] ?? '',
                    'phone' => $g['phone'] ?? '',
                    'fax' => $g['fax'] ?? '',
                ];
            }
        }

        try {
            $this->apiRequest("domains/{$domain}/contacts", 'PATCH', $contacts);

            Log::info('GoDaddy: Contacts updated', [
                'domain' => $domain,
            ]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_set_contacts_success'),
                'data' => [
                    'domain' => $domain,
                    'groups' => $groups,
                    'updated_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    // -------------------------------------------------------------------------
    // Domain Lock
    // -------------------------------------------------------------------------

    /**
     * Get domain lock status
     *
     * Expected params:
     *  - domain (string): Full domain name
     */
    public function getDomainLock(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest("domains/{$domain}");

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_get_lock_success'),
                'data' => [
                    'domain' => $domain,
                    'locked' => (bool) ($response['locked'] ?? false),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Update domain lock status
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - lock_status (bool): Whether to lock the domain
     */
    public function updateDomainLock(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];
        $lockStatus = (bool) ($params['lock_status'] ?? true);

        try {
            $this->apiRequest("domains/{$domain}", 'PATCH', [
                'locked' => $lockStatus,
            ]);

            Log::info('GoDaddy: Domain lock updated', [
                'domain' => $domain,
                'locked' => $lockStatus,
            ]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_set_lock_success'),
                'data' => [
                    'domain' => $domain,
                    'lock_status' => $lockStatus,
                    'updated_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    // -------------------------------------------------------------------------
    // Privacy / ID Protection
    // -------------------------------------------------------------------------

    /**
     * Get privacy settings
     *
     * Expected params:
     *  - domain (string): Full domain name
     */
    public function getPrivacySettings(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest("domains/{$domain}");

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_get_privacy_success'),
                'data' => [
                    'domain' => $domain,
                    'privacy_enabled' => (bool) ($response['privacy'] ?? false),
                    'available' => true,
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Update privacy settings (enable/disable WHOIS privacy)
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - privacy_enabled (bool): Enable or disable privacy
     */
    public function updatePrivacySettings(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];
        $privacyEnabled = (bool) ($params['privacy_enabled'] ?? true);
        $tld = $this->extractTld($domain);

        try {
            if ($privacyEnabled) {
                // Purchase privacy - requires agreement consent
                $agreementKeys = $this->getAgreementKeys($tld, true, false);

                $body = [
                    'consent' => [
                        'agreedAt' => now()->toIso8601String(),
                        'agreedBy' => request()->ip() ?? '127.0.0.1',
                        'agreementKeys' => $agreementKeys,
                    ],
                ];

                $this->apiRequest("domains/{$domain}/privacy/purchase", 'POST', $body);
            } else {
                // Disable privacy — expose WHOIS. WiseCP (modifyPrivacyProtection)
                // + HostBill (updateIDprotection) both PATCH domains/{domain}
                // {exposeWhois:true}; HostBill attaches an EXPOSE_WHOIS consent.
                $this->apiRequest("domains/{$domain}", 'PATCH', [
                    'exposeWhois' => true,
                    'consent' => [
                        'agreedAt' => now()->toIso8601String(),
                        'agreedBy' => request()->ip() ?? '127.0.0.1',
                        'agreementKeys' => ['EXPOSE_WHOIS'],
                    ],
                ]);
            }

            Log::info('GoDaddy: Privacy settings updated', [
                'domain' => $domain,
                'privacy_enabled' => $privacyEnabled,
            ]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_set_privacy_success'),
                'data' => [
                    'domain' => $domain,
                    'privacy_enabled' => $privacyEnabled,
                    'updated_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    // -------------------------------------------------------------------------
    // EPP Code
    // -------------------------------------------------------------------------

    /**
     * Get the EPP/Auth code for a domain
     *
     * Expected params:
     *  - domain (string): Full domain name
     */
    public function getEppCode(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest("domains/{$domain}");

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_get_epp_success'),
                'data' => [
                    'domain' => $domain,
                    'epp_code' => $response['authCode'] ?? '',
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    // -------------------------------------------------------------------------
    // Domain Availability
    // -------------------------------------------------------------------------

    /**
     * Check if a domain is available for registration
     *
     * Expected params:
     *  - domain (string): Full domain name to check
     */
    public function checkDomainAvailability(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest('domains/available', 'GET', [], [
                'domain' => $domain,
            ]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_check_availability_success'),
                'data' => [
                    'domain' => $response['domain'] ?? $domain,
                    'available' => (bool) ($response['available'] ?? false),
                    // GoDaddy returns price in micro-units (1e6). Normalize to a
                    // major-currency decimal (e.g. 11990000 → 11.99).
                    'price' => isset($response['price']) ? $response['price'] / 1000000 : null,
                    'currency' => $response['currency'] ?? 'USD',
                    'period' => $response['period'] ?? 1,
                    'definitive' => $response['definitive'] ?? false,
                    'checked_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Get domain name suggestions
     *
     * Expected params:
     *  - domain (string): Seed domain to get suggestions for
     */
    public function checkDomainSuggestions(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest('domains/suggest', 'GET', [], [
                'query' => $domain,
                'limit' => $params['limit'] ?? 10,
            ]);

            $suggestions = [];
            if (is_array($response)) {
                foreach ($response as $suggestion) {
                    $suggestions[] = [
                        'domain' => $suggestion['domain'] ?? '',
                    ];
                }
            }

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_suggestions_success'),
                'data' => [
                    'domain' => $domain,
                    'suggestions' => $suggestions,
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    // -------------------------------------------------------------------------
    // Domain Details & Sync
    // -------------------------------------------------------------------------

    /**
     * Get full domain details from the registrar
     *
     * Expected params:
     *  - domain (string): Full domain name
     */
    public function getDomainDetails(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest("domains/{$domain}");

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_get_domain_details_success'),
                'data' => [
                    'domain' => $response['domain'] ?? $domain,
                    'domain_id' => $response['domainId'] ?? null,
                    'status' => $this->normalizeStatus($response['status'] ?? ''),
                    'locked' => (bool) ($response['locked'] ?? false),
                    'privacy_enabled' => (bool) ($response['privacy'] ?? false),
                    'auto_renew' => (bool) ($response['renewAuto'] ?? false),
                    'created_at' => $response['createdAt'] ?? null,
                    'updated_at' => $response['modifiedAt'] ?? null,
                    'expires_at' => $response['expires'] ?? null,
                    'nameservers' => $response['nameServers'] ?? [],
                    'registrar' => 'GoDaddy',
                    'epp_code_available' => ! empty($response['authCode']),
                    'registrant_email' => $response['contactRegistrant']['email'] ?? null,
                    'transfer_away_eligible_at' => $response['transferAwayEligibleAt'] ?? null,
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Sync domain data from the registrar
     *
     * Expected params:
     *  - domain (string): Full domain name
     */
    public function syncDomain(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest("domains/{$domain}");

            $status = $this->normalizeStatus($response['status'] ?? '');

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_sync_success'),
                'data' => [
                    'domain' => $domain,
                    'status' => $status,
                    'expires_at' => $response['expires'] ?? null,
                    'auto_renew' => (bool) ($response['renewAuto'] ?? false),
                    'locked' => (bool) ($response['locked'] ?? false),
                    'privacy_enabled' => (bool) ($response['privacy'] ?? false),
                    'synced_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            // Check if domain was transferred away
            if (str_contains($e->getMessage(), 'not found')) {
                return [
                    'status' => 'success',
                    'message' => __('admin/modules.godaddy_sync_transferred_away'),
                    'data' => [
                        'domain' => $domain,
                        'status' => 'transferred_away',
                        'synced_at' => now()->toISOString(),
                    ],
                ];
            }

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

    /**
     * Sync domain transfer status
     *
     * Expected params:
     *  - domain (string): Full domain name
     */
    public function syncDomainTransfer(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];

        try {
            $response = $this->apiRequest("domains/{$domain}");

            $status = $this->normalizeStatus($response['status'] ?? '');

            $transferStatus = 'pending';
            if ($status === 'active') {
                $transferStatus = 'completed';
            } elseif (in_array($status, ['expired', 'cancelled'])) {
                $transferStatus = 'failed';
            }

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_sync_transfer_success'),
                'data' => [
                    'domain' => $domain,
                    'transfer_status' => $transferStatus,
                    'expires_at' => $response['expires'] ?? null,
                    'synced_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    // -------------------------------------------------------------------------
    // DNS Management
    // -------------------------------------------------------------------------

    /**
     * Get DNS records for a domain
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - type (string, optional): Record type filter (A, AAAA, CNAME, MX, TXT, NS, SRV)
     */
    public function getDNSRecords(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];
        $endpoint = "domains/{$domain}/records";

        if (! empty($params['type'])) {
            $endpoint .= '/' . strtoupper($params['type']);
        }

        try {
            $response = $this->apiRequest($endpoint);

            $records = [];
            if (is_array($response)) {
                foreach ($response as $record) {
                    $records[] = [
                        'type' => $record['type'] ?? '',
                        'name' => $record['name'] ?? '',
                        'value' => $record['data'] ?? '',
                        'ttl' => $record['ttl'] ?? 3600,
                        'priority' => $record['priority'] ?? null,
                    ];
                }
            }

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_get_dns_success'),
                'data' => [
                    'domain' => $domain,
                    'records' => $records,
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Add a DNS record
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - type (string): Record type (A, AAAA, CNAME, MX, TXT, NS, SRV)
     *  - name (string): Record name
     *  - value (string): Record value/data
     *  - ttl (int, optional): TTL in seconds (default 3600)
     *  - priority (int, optional): Priority (for MX, SRV)
     */
    public function addDNSRecord(array $params)
    {
        $requiredParams = ['domain', 'type', 'name', 'value'];
        foreach ($requiredParams as $param) {
            if (empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.godaddy_missing_param', ['param' => $param]),
                ];
            }
        }

        $domain = $params['domain'];
        $type = strtoupper($params['type']);

        $record = [
            'type' => $type,
            'name' => $params['name'],
            'data' => $params['value'],
            'ttl' => (int) ($params['ttl'] ?? 3600),
        ];

        if (isset($params['priority'])) {
            $record['priority'] = (int) $params['priority'];
        }

        try {
            $this->apiRequest("domains/{$domain}/records", 'PATCH', [$record]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_add_dns_success'),
                'data' => [
                    'domain' => $domain,
                    'type' => $type,
                    'name' => $params['name'],
                    'value' => $params['value'],
                    'ttl' => $record['ttl'],
                    'priority' => $record['priority'] ?? null,
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Update a DNS record (replaces all records for a given type and name)
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - type (string): Record type
     *  - name (string): Record name
     *  - value (string): New record value/data
     *  - ttl (int, optional): TTL in seconds
     *  - priority (int, optional): Priority
     */
    public function updateDNSRecord(array $params)
    {
        $requiredParams = ['domain', 'type', 'name'];
        foreach ($requiredParams as $param) {
            if (empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.godaddy_missing_param', ['param' => $param]),
                ];
            }
        }

        $domain = $params['domain'];
        $type = strtoupper($params['type']);
        $name = $params['name'];

        $record = [
            'data' => $params['value'] ?? '',
            'ttl' => (int) ($params['ttl'] ?? 3600),
        ];

        if (isset($params['priority'])) {
            $record['priority'] = (int) $params['priority'];
        }

        try {
            $this->apiRequest("domains/{$domain}/records/{$type}/{$name}", 'PUT', [$record]);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_update_dns_success'),
                'data' => [
                    'domain' => $domain,
                    'type' => $type,
                    'name' => $name,
                    'value' => $record['data'],
                    'ttl' => $record['ttl'],
                    'priority' => $record['priority'] ?? null,
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Delete a DNS record
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - type (string): Record type
     *  - name (string): Record name
     */
    public function deleteDNSRecord(array $params)
    {
        $requiredParams = ['domain', 'type', 'name'];
        foreach ($requiredParams as $param) {
            if (empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.godaddy_missing_param', ['param' => $param]),
                ];
            }
        }

        $domain = $params['domain'];
        $type = strtoupper($params['type']);
        $name = $params['name'];

        try {
            $this->apiRequest("domains/{$domain}/records/{$type}/{$name}", 'DELETE');

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_delete_dns_success'),
                'data' => [
                    'domain' => $domain,
                    'type' => $type,
                    'name' => $name,
                    'deleted_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Bulk update DNS records (replace all records for the domain)
     *
     * Expected params:
     *  - domain (string): Full domain name
     *  - records (array): Array of DNS records
     */
    public function updateDNSRecords(array $params)
    {
        if (empty($params['domain'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.godaddy_missing_param', ['param' => 'domain']),
            ];
        }

        $domain = $params['domain'];
        $records = $params['records'] ?? [];

        // Transform records to GoDaddy format
        $apiRecords = [];
        foreach ($records as $record) {
            $apiRecord = [
                'type' => strtoupper($record['type'] ?? 'A'),
                'name' => $record['name'] ?? '@',
                'data' => $record['value'] ?? '',
                'ttl' => (int) ($record['ttl'] ?? 3600),
            ];

            if (isset($record['priority'])) {
                $apiRecord['priority'] = (int) $record['priority'];
            }

            $apiRecords[] = $apiRecord;
        }

        try {
            $this->apiRequest("domains/{$domain}/records", 'PUT', $apiRecords);

            return [
                'status' => 'success',
                'message' => __('admin/modules.godaddy_update_dns_records_success'),
                'data' => [
                    'domain' => $domain,
                    'records' => $records,
                    'updated_at' => now()->toISOString(),
                ],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => $e->getMessage(),
            ];
        }
    }

    // -------------------------------------------------------------------------
    // Helper Methods
    // -------------------------------------------------------------------------

    /**
     * Extract TLD from a domain name
     */
    protected function extractTld(string $domain): string
    {
        $parts = explode('.', $domain, 2);

        return $parts[1] ?? $parts[0];
    }

    /**
     * Fetch agreement keys from GoDaddy for a given TLD
     *
     * @param  string  $tld  The TLD (e.g. "com", "net")
     * @param  bool  $privacy  Whether privacy is being purchased
     * @param  bool  $forTransfer  Whether this is for a transfer
     * @return array Array of agreement key strings
     *
     * @throws \Exception
     */
    protected function getAgreementKeys(string $tld, bool $privacy = false, bool $forTransfer = false): array
    {
        $query = [
            'tlds' => $tld,
            'privacy' => $privacy ? 'true' : 'false',
            'forTransfer' => $forTransfer ? 'true' : 'false',
        ];

        $url = $this->getApiUrl() . 'domains/agreements?' . http_build_query($query);

        $response = Http::withHeaders([
            'Authorization' => $this->getAuthHeader(),
            'Accept' => 'application/json',
            'X-Market-Id' => 'en-US',
        ])->timeout(30)->get($url);

        if ($response->failed()) {
            throw new \Exception($response->json('message') ?? __('admin/modules.godaddy_agreement_fetch_failed'));
        }

        $agreements = $response->json() ?? [];
        $keys = [];

        foreach ($agreements as $agreement) {
            if (isset($agreement['agreementKey'])) {
                $keys[] = $agreement['agreementKey'];
            }
        }

        return $keys;
    }

    /**
     * Map procp's flat `additional_fields` (client_domain_fields, WHMCS-style
     * human-readable keys) to the TLD-specific extra attributes GoDaddy's
     * purchase/transfer body requires. Mirrors WHMCS godaddy_additional_fields
     * (godaddy.php:476-707) field-by-field: .es/.com.es/.nom.es/.org.es → entityType/
     * identificationType/identificationNumber; .nyc → entityType; .sg →
     * adminPersonalId/identificationNumber; .us → entityType (Nexus intent) +
     * identification; .ca → entityType (Legal Type) + identification; .eu →
     * entityType. Returns an empty array for TLDs without extra requirements.
     *
     * @param  string  $tld     TLD extracted from the domain string (no leading dot)
     * @param  array<string,mixed>  $fields  Flat field_key => value map
     * @return array<string,mixed> GoDaddy body attributes to array_merge in
     */
    protected function mapAdditionalFields(string $tld, array $fields): array
    {
        if (empty($fields)) {
            return [];
        }

        $tld = strtolower(ltrim($tld, '.'));
        $out = [];

        switch ($tld) {
            case 'es':
            case 'com.es':
            case 'nom.es':
            case 'org.es':
                $out['entityType'] = $fields['Entity Type'] ?? null;
                $out['identificationType'] = $this->mapEsIdType($fields['ID Form Type'] ?? '');
                $out['identificationNumber'] = $fields['ID Form Number'] ?? null;
                break;

            case 'nyc':
                // WHMCS: ORGANIZATION when a company is present, else INDIVIDUAL.
                $out['entityType'] = ! empty($fields['Company Name']) || ! empty($fields['Organization'])
                    ? 'ORGANIZATION'
                    : 'INDIVIDUAL';
                break;

            case 'sg':
                $out['adminPersonalId'] = $fields['Admin Personal ID'] ?? null;
                $out['identificationNumber'] = $fields['RCB Singapore ID'] ?? null;
                break;

            case 'us':
                $out['entityType'] = $this->mapUsNexusIntent($fields['Nexus Category'] ?? '');
                $out['identificationType'] = $this->mapEsIdType($fields['ID Form Type'] ?? '');
                $out['identificationNumber'] = $fields['ID Form Number'] ?? null;
                break;

            case 'ca':
                $out['entityType'] = $this->mapCaLegalType($fields['Legal Type'] ?? '');
                $out['identificationType'] = $this->mapEsIdType($fields['ID Form Type'] ?? '');
                $out['identificationNumber'] = $fields['ID Form Number'] ?? null;
                break;

            case 'eu':
                $out['entityType'] = $fields['Entity Type'] ?? null;
                break;
        }

        // Drop unset/empty attributes — GoDaddy rejects null extra fields.
        return array_filter($out, static fn ($v) => $v !== null && $v !== '');
    }

    /**
     * Declare the TLD-specific extra registration fields GoDaddy requires for
     * requirement TLDs (.us/.ca/.eu/.es family/.sg). Without this override the
     * store/basket modal never collects these inputs, mapAdditionalFields()
     * receives an empty $fields, and GoDaddy rejects the purchase/transfer for
     * these TLDs.
     *
     * Field `name` keys MUST match exactly what mapAdditionalFields() reads
     * ('Nexus Category', 'ID Form Type', 'ID Form Number', 'Entity Type',
     * 'Legal Type', 'Admin Personal ID', 'RCB Singapore ID'). Mirrors the
     * WHMCS godaddy additionalfields contract (godaddy.php:476-707) and the
     * sibling Namecheap/Enom/Opensrs list-shape.
     *
     * @param  string  $tld  TLD extracted from the domain string (may include a leading dot)
     * @return array<int,array<string,mixed>> WHMCS-style field definitions
     */
    public function getExtraFields(string $tld): array
    {
        $tld = strtolower(ltrim($tld, '.'));

        // ID Form Type option list (→ mapEsIdType): CITIZEN/COMPANY/OTHER.
        $idFormType = [
            'name' => 'ID Form Type', 'type' => 'dropdown', 'required' => true,
            'options' => 'Tax Identification Number,Tax Identification Code,Foreigner Identification Number,Other Identification',
        ];
        $idFormNumber = ['name' => 'ID Form Number', 'type' => 'text', 'required' => true];

        return match (true) {
            // .us — Nexus Category intent (→ mapUsNexusIntent) + identification.
            $tld === 'us' => [
                ['name' => 'Nexus Category', 'type' => 'dropdown', 'required' => true,
                    'options' => 'C11,C12,C21,C31,C32'],
                $idFormType,
                $idFormNumber,
            ],

            // .ca — CIRA Legal Type (→ mapCaLegalType) + identification.
            $tld === 'ca' => [
                ['name' => 'Legal Type', 'type' => 'dropdown', 'required' => true,
                    'options' => 'Corporation,Canadian Citizen,Permanent Resident of Canada,Government,Canadian Educational Institution,Canadian Unincorporated Association,Canadian Hospital,Partnership Registered in Canada,Trade-mark registered in Canada,Canadian Trade Union,Canadian Political Party,Canadian Library Archive or Museum,Trust established in Canada,Aboriginal Peoples,Legal Representative of a Canadian Citizen,Official mark registered in Canada'],
                $idFormType,
                $idFormNumber,
            ],

            // .eu — entity type only.
            $tld === 'eu' => [
                ['name' => 'Entity Type', 'type' => 'text', 'required' => true],
            ],

            // .es family — entity type + identification.
            in_array($tld, ['es', 'com.es', 'nom.es', 'org.es'], true) => [
                ['name' => 'Entity Type', 'type' => 'text', 'required' => true],
                $idFormType,
                $idFormNumber,
            ],

            // .sg — admin personal id + RCB id.
            $tld === 'sg' => [
                ['name' => 'Admin Personal ID', 'type' => 'text', 'required' => false],
                ['name' => 'RCB Singapore ID', 'type' => 'text', 'required' => false],
            ],

            // .nyc — entityType is auto-derived from company presence; no field.
            default => [],
        };
    }

    /**
     * WHMCS godaddy_additional_fields ID-Form-Type → GoDaddy identificationType
     * (godaddy.php:692-704).
     */
    protected function mapEsIdType(string $idType): string
    {
        switch ($idType) {
            case 'Tax Identification Number':
                return 'CITIZEN';
            case 'Tax Identification Code':
                return 'COMPANY';
            case 'Foreigner Identification Number':
            case 'Other Identification':
            default:
                return 'OTHER';
        }
    }

    /**
     * WHMCS godaddy_additional_fields .us Nexus Category → GoDaddy entityType
     * (godaddy.php:616-634).
     */
    protected function mapUsNexusIntent(string $intent): string
    {
        switch ($intent) {
            case 'C12':
                return 'PERMANENT_RESIDENT';
            case 'C21':
                return 'INCORPORATED';
            case 'C31':
                return 'FOREIGN_BUSINESS';
            case 'C32':
                return 'FOREIGN_OFFICE';
            case 'C11':
            default:
                return 'CITIZEN';
        }
    }

    /**
     * WHMCS godaddy_additional_fields .ca Legal Type → GoDaddy entityType
     * (godaddy.php:636-686).
     */
    protected function mapCaLegalType(string $legalType): string
    {
        $map = [
            'Canadian Citizen' => 'CITIZEN',
            'Permanent Resident of Canada' => 'RESIDENT_PERMANENT',
            'Government' => 'GOVERNMENT',
            'Canadian Educational Institution' => 'EDUCATIONAL',
            'Canadian Unincorporated Association' => 'ASSOCIATION',
            'Canadian Hospital' => 'HOSPITAL',
            'Partnership Registered in Canada' => 'PARTNERSHIP',
            'Trade-mark registered in Canada' => 'MARK_TRADE',
            'Canadian Trade Union' => 'UNION',
            'Canadian Political Party' => 'POLITICAL_PARTY',
            'Canadian Library Archive or Museum' => 'LIBRARY_ARCHIVE_MUSEUM',
            'Trust established in Canada' => 'TRUST',
            'Aboriginal Peoples' => 'ABORIGINAL',
            'Legal Representative of a Canadian Citizen' => 'LEGAL_REPRESENTATIVE',
            'Official mark registered in Canada' => 'MARK_REGISTERED',
            'Corporation' => 'CORPORATION',
        ];

        return $map[$legalType] ?? 'CORPORATION';
    }

    /**
     * Build the GoDaddy contacts payload from the PRC-969 canonical contact
     * groups. Each of the four GoDaddy contact slots (registrant/admin/tech/
     * billing) is sourced from `$this->contact($params, <group>)`, which
     * returns a POPULATED canonical contact (with field-level client_*
     * fallback). Per-group registrant fallback is applied when a group is not
     * present in the incoming params.
     *
     * Pure + testable: takes the full provisioning $params, returns the exact
     * `contactRegistrant`/`contactAdmin`/`contactTech`/`contactBilling` shape
     * that `array_merge($body, ...)` expects at register/transfer.
     *
     * @param  array  $params  Full provisioning params (PRC-969 enriched)
     * @return array<string,array> GoDaddy-formatted contacts
     */
    protected function buildProvisioningContacts(array $params): array
    {
        $slotMap = [
            'registrant' => 'contactRegistrant',
            'admin' => 'contactAdmin',
            'tech' => 'contactTech',
            'billing' => 'contactBilling',
        ];

        // Registrant is always resolved (used as the per-group fallback source).
        $registrant = $this->contact($params, 'registrant');

        $payload = [];
        foreach ($slotMap as $group => $apiKey) {
            $groupPresent = (isset($params['contacts'][$group]) && is_array($params['contacts'][$group]))
                || (isset($params[$group]) && is_array($params[$group]));

            $canonical = ($group === 'registrant' || $groupPresent)
                ? $this->contact($params, $group)
                : $registrant;

            $payload[$apiKey] = $this->mapCanonicalContactToGoddaddy($canonical);
        }

        return $payload;
    }

    /**
     * Map a single canonical contact (first_name/last_name/company/email/phone/
     * address1/address2/city/state/postcode/country_code) to the GoDaddy
     * contact shape.
     *
     * GoDaddy contact = {nameFirst, nameLast, organization, email, phone,
     * addressMailing:{address1, address2, city, state, postalCode, country}}.
     * Verified against WHMCS godaddy.php (godaddy_contacts / setContactDetails).
     *
     * @param  array<string,string>  $c  Canonical contact
     * @return array<string,mixed> GoDaddy contact
     */
    protected function mapCanonicalContactToGoddaddy(array $c): array
    {
        return [
            'nameFirst' => $c['first_name'] ?? '',
            'nameLast' => $c['last_name'] ?? '',
            'organization' => $c['company'] ?? '',
            'email' => $c['email'] ?? '',
            'phone' => $this->formatPhoneForGoddaddy($c['phone'] ?? '', $c['country_code'] ?? ''),
            'addressMailing' => [
                'address1' => $c['address1'] ?? '',
                'address2' => $c['address2'] ?? '',
                'city' => $c['city'] ?? '',
                'state' => $c['state'] ?? '',
                'postalCode' => $c['postcode'] ?? '',
                'country' => $c['country_code'] ?? '',
            ],
        ];
    }

    /**
     * Format a canonical phone number into GoDaddy's required `+CC.NUMBER`
     * (E.164-with-dot) form, e.g. `+905551112233` → `+90.5551112233`.
     *
     * The canonical phone is a single E.164-ish string; GoDaddy expects the
     * country code and subscriber number joined by a dot. This is non-lossy:
     * the same digits are preserved, only a separator is introduced. When the
     * phone already contains a `.` separator it is kept as-is (WHMCS
     * `fullphonenumber` is already `+cc.number`).
     *
     * @param  string  $phone         Canonical phone (e.g. +905551112233)
     * @param  string  $countryCode   ISO-2 country code, used to infer the
     *                                 dial-code split length when possible
     */
    protected function formatPhoneForGoddaddy(string $phone, string $countryCode = ''): string
    {
        $phone = trim($phone);
        if ($phone === '') {
            return '';
        }

        // Already in +cc.number form → keep, normalizing whitespace and the
        // leading +. E.g. "+90 .555 111" → "+90.555111".
        if (strpos($phone, '.') !== false) {
            $normalized = str_replace(' ', '', $phone);

            return '+' . ltrim($normalized, '+');
        }

        // Strip everything but digits.
        $digits = preg_replace('/\D+/', '', $phone);
        if ($digits === '') {
            return '';
        }

        $cc = $this->dialCodeForCountry($countryCode);

        if ($cc !== '') {
            // Country dial code is authoritative. If the number already carries
            // it as a prefix, strip it; otherwise it's a national number — drop
            // the trunk-0 (e.g. TR "0555…" → "555…") and prepend the real CC.
            if (strncmp($digits, $cc, strlen($cc)) === 0 && strlen($digits) > strlen($cc)) {
                $rest = substr($digits, strlen($cc));
            } else {
                $rest = ltrim($digits, '0');
            }
            $ccPart = $cc;
        } else {
            // Unknown country (not in the dial map) → E.164 length heuristic.
            // Digits are preserved (non-lossy) but the CC split may be approximate.
            $ccLen = strlen($digits) > 10 ? strlen($digits) - 10 : 1;
            $ccLen = max(1, min(3, $ccLen));
            $ccPart = substr($digits, 0, $ccLen);
            $rest = substr($digits, $ccLen);
        }

        if ($rest === '' || $rest === false) {
            // Could not split meaningfully — emit the raw digits behind +.
            return '+' . $digits;
        }

        return '+' . $ccPart . '.' . $rest;
    }

    /**
     * Best-effort ISO-2 → numeric dial-code lookup for the phone splitter.
     * Only the common set is needed; unknown codes fall through to the
     * length heuristic in formatPhoneForGoddaddy().
     */
    protected function dialCodeForCountry(string $countryCode): string
    {
        $map = [
            'TR' => '90', 'US' => '1', 'CA' => '1', 'GB' => '44', 'DE' => '49',
            'FR' => '33', 'NL' => '31', 'ES' => '34', 'IT' => '39', 'AU' => '61',
            'IN' => '91', 'RU' => '7', 'BR' => '55', 'CN' => '86', 'JP' => '81',
            'AT' => '43', 'BE' => '32', 'BG' => '359', 'CH' => '41', 'CY' => '357',
            'CZ' => '420', 'DK' => '45', 'EE' => '372', 'FI' => '358', 'GR' => '30',
            'HR' => '385', 'HU' => '36', 'IE' => '353', 'IS' => '354', 'LT' => '370',
            'LU' => '352', 'LV' => '371', 'MT' => '356', 'NO' => '47', 'PL' => '48',
            'PT' => '351', 'RO' => '40', 'RS' => '381', 'SE' => '46', 'SI' => '386',
            'SK' => '421', 'UA' => '380',
            'AE' => '971', 'SA' => '966', 'QA' => '974', 'KW' => '965', 'IL' => '972',
            'EG' => '20', 'ZA' => '27', 'NG' => '234', 'KE' => '254', 'MA' => '212',
            'AZ' => '994', 'GE' => '995', 'KZ' => '7',
            'HK' => '852', 'SG' => '65', 'MY' => '60', 'ID' => '62', 'TH' => '66',
            'VN' => '84', 'PH' => '63', 'KR' => '82', 'TW' => '886', 'PK' => '92',
            'BD' => '880', 'LK' => '94', 'NZ' => '64',
            'MX' => '52', 'AR' => '54', 'CL' => '56', 'CO' => '57', 'PE' => '51',
            'VE' => '58', 'UY' => '598',
        ];

        return $map[strtoupper(trim($countryCode))] ?? '';
    }

    /**
     * Normalize GoDaddy domain status to procp status string
     *
     * @param  string  $status  GoDaddy status string
     * @return string Normalized status
     */
    protected function normalizeStatus(string $status): string
    {
        $status = strtoupper($status);

        switch ($status) {
            case 'ACTIVE':
                return 'active';

            case 'AWAITING':
            case 'PENDING':
            case 'PENDING_UPDATE':
            case 'RESERVED':
            case 'REVERTED':
            case 'UNLOCKED':
            case 'UNPARKED':
            case 'UPDATED':
                return 'pending';

            case 'CANCELLED':
            case 'EXPIRED':
            case 'FAILED':
                return 'expired';

            case 'CONFISCATED':
            case 'DISABLED':
            case 'EXCLUDED':
            case 'HELD':
            case 'LOCKED':
            case 'PARKED':
            case 'SUSPENDED':
            case 'TRANSFERRED':
                return 'suspended';

            default:
                return 'active';
        }
    }
}
