<?php

namespace App\Modules\Registrar\Namecom;

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

class Namecom extends AbstractRegistrar
{
    /**
     * Live API endpoint
     */
    protected string $liveUrl = 'https://api.name.com/v4';

    /**
     * Test/dev API endpoint
     */
    protected string $testUrl = 'https://api.dev.name.com/v4';

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

    /**
     * Get the active API URL based on test mode setting
     */
    protected function getApiUrl(): string
    {
        return ! empty($this->config['test_mode']) && $this->config['test_mode'] === 'true'
            ? $this->testUrl
            : $this->liveUrl;
    }

    /**
     * Module configuration fields
     */
    public function getConfigFields(): array
    {
        return [
            'username' => [
                'type' => 'text',
                'label' => __('admin/modules.namecom_field_username_label'),
                'required' => true,
                'description' => __('admin/modules.namecom_field_username_description'),
                'placeholder' => 'your-username',
                'value' => $this->config['username'] ?? '',
            ],
            'token' => [
                'type' => 'password',
                'label' => __('admin/modules.namecom_field_token_label'),
                'required' => true,
                'description' => __('admin/modules.namecom_field_token_description'),
                'placeholder' => '********',
                'value' => $this->config['token'] ?? '',
            ],
            'test_mode' => [
                'type' => 'checkbox',
                'label' => __('admin/modules.namecom_field_test_mode_label'),
                'required' => false,
                'description' => __('admin/modules.namecom_field_test_mode_description'),
                'value' => $this->config['test_mode'] ?? false,
            ],
        ];
    }

    /**
     * Validate module configuration
     */
    public function validateConfig()
    {
        if (empty($this->config['username'])) {
            throw new \Exception(__('admin/modules.namecom_config_username_missing'));
        }

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

        return true;
    }

    // -------------------------------------------------------------------------
    //  TLD-specific Extra Fields
    // -------------------------------------------------------------------------

    /**
     * Declare the TLD-specific extra-field schema for restricted TLDs so the
     * core order/basket flow collects them into client_domain_fields (which the
     * builder emits as `additional_fields`, forwarded verbatim as
     * `tldRequirements` by registerDomain()). Without these, name.com rejects
     * restricted TLD registrations.
     *
     * IMPORTANT (name.com contract): unlike eNom (which translates human labels
     * into API codes via populateAdditionalFields), registerDomain() forwards
     * `additional_fields` UNCHANGED into `tldRequirements`. The core stores each
     * field under its ARRAY KEY (client_domain_fields.field_key), and that key is
     * what flows into `tldRequirements`. Therefore each field KEY MUST be the exact
     * name.com `tldRequirements` API key, and dropdown option KEYS (value) MUST be
     * the API codes — not human labels.
     *
     * Schema = canonical RegistrarInterface::getExtraFields shape (assoc keyed by
     * field_key; each entry ['type','label','options'=>['code'=>'label'],'required']).
     * Verified against HostBill namecom_v2 getExtendedAttributes()
     * (nexus_apppurpose / nexus_validator, P1..P5) and the name.com v4 CreateDomain
     * tldRequirements reference.
     */
    public function getExtraFields(string $tld): array
    {
        $t = strtolower(ltrim($tld, '.'));
        // name.com keys on the last TLD segment (co.uk → uk).
        $last = str_contains($t, '.') ? (explode('.', $t)[array_key_last(explode('.', $t))]) : $t;

        return match ($last) {
            // .us nexus (verified: HostBill namecom_v2). Keys/values are the raw
            // name.com tldRequirements codes (P1..P5 purpose, C-code category).
            'us' => [
                'nexus_apppurpose' => [
                    'type' => 'select',
                    'label' => 'Application Purpose',
                    'required' => true,
                    'options' => [
                        'P1' => 'For Profit Business',
                        'P2' => 'Non-profit Business',
                        'P3' => 'Personal Use',
                        'P4' => 'Educational Purposes',
                        'P5' => 'Government Purposes',
                    ],
                    'order' => 10,
                ],
                'nexus_validator' => [
                    'type' => 'select',
                    'label' => 'Nexus Category',
                    'required' => true,
                    'options' => [
                        'C11' => 'US Citizen',
                        'C12' => 'US Permanent Resident',
                        'C21' => 'US Entity / Organization',
                        'C31' => 'Foreign org with regular US activity',
                        'C32' => 'Foreign org with US office',
                    ],
                    'order' => 20,
                ],
            ],
            // .ca CIRA requirements (name.com v4 tldRequirements keys).
            'ca' => [
                'ca_legaltype' => [
                    'type' => 'text',
                    'label' => 'CIRA Legal Type',
                    'required' => true,
                    'order' => 10,
                ],
                'ca_agreementversion' => [
                    'type' => 'text',
                    'label' => 'CIRA Agreement Version',
                    'required' => true,
                    'order' => 20,
                ],
                'ca_agreementvalue' => [
                    'type' => 'text',
                    'label' => 'CIRA Agreement Accepted (y/n)',
                    'required' => true,
                    'order' => 30,
                ],
            ],
            // .eu trustee/country (name.com v4 tldRequirements keys).
            'eu' => [
                'eu_accepttrustee' => [
                    'type' => 'text',
                    'label' => 'Accept EU Trustee Service (y/n)',
                    'required' => false,
                    'order' => 10,
                ],
                'eu_country' => [
                    'type' => 'text',
                    'label' => 'EU Country of Citizenship (2-letter)',
                    'required' => false,
                    'order' => 20,
                ],
            ],
            default => [],
        };
    }

    // -------------------------------------------------------------------------
    //  API Communication
    // -------------------------------------------------------------------------

    /**
     * Make API request to Name.com v4 REST API
     *
     * @param  string  $endpoint  API endpoint path (e.g. /domains)
     * @param  string  $method  HTTP method (GET, POST, PUT, DELETE)
     * @param  array  $data  Request body data
     * @return array Response with status, message, data keys
     */
    protected function callApi($endpoint, $method = 'GET', $data = [])
    {
        $url = rtrim($this->getApiUrl(), '/') . '/' . ltrim($endpoint, '/');
        $username = $this->config['username'] ?? '';
        $token = $this->config['token'] ?? '';

        Log::debug('Name.com API request', [
            'method' => $method,
            'endpoint' => $endpoint,
        ]);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_USERPWD, $username . ':' . $token);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Accept: application/json',
        ]);

        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        } elseif ($method === 'PUT') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        } elseif ($method === 'DELETE') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        }

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

        if ($curlErrno || ! $response) {
            Log::error('Name.com: cURL error', ['errno' => $curlErrno, 'error' => $curlError]);

            return $this->errorResponse(
                __('admin/modules.namecom_curl_error', ['error' => $curlError ?: 'No response'])
            );
        }

        $decoded = json_decode($response, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            Log::error('Name.com: Invalid JSON response', ['response' => substr($response, 0, 500)]);

            return $this->errorResponse(__('admin/modules.namecom_invalid_response'));
        }

        // Name.com returns error details in the response body for non-2xx codes
        if ($httpCode >= 400) {
            $errorMessage = $decoded['message'] ?? $decoded['details'] ?? 'Unknown API error';
            Log::error('Name.com: API error', [
                'http_code' => $httpCode,
                'message' => $errorMessage,
                'endpoint' => $endpoint,
            ]);

            return $this->errorResponse($errorMessage);
        }

        return [
            'status' => 'success',
            'data' => $decoded,
        ];
    }

    // -------------------------------------------------------------------------
    //  Response Helpers
    // -------------------------------------------------------------------------

    /**
     * Build a standard success response
     */
    protected function successResponse(string $message, array $data = []): array
    {
        return [
            'status' => 'success',
            'message' => $message,
            'data' => $data,
        ];
    }

    /**
     * Build a standard error response
     */
    protected function errorResponse(string $message, array $data = []): array
    {
        return [
            'status' => 'error',
            'message' => $message,
            'data' => $data,
        ];
    }

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

    /**
     * Register a new domain
     * POST /domains
     */
    public function registerDomain(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $years = (int) ($params['period'] ?? $params['years'] ?? 1);
            $nameservers = $params['nameservers'] ?? [];

            $requestData = [
                'domain' => [
                    'domainName' => $domain,
                ],
                'years' => $years,
            ];

            // premium purchasePrice only when present (null bloats the envelope).
            if (! empty($params['purchase_price'])) {
                $requestData['purchasePrice'] = $params['purchase_price'];
            }

            if (! empty($nameservers)) {
                $requestData['domain']['nameservers'] = array_values($nameservers);
            }

            // Build contacts from the four canonical PRC-969 groups (registrant/admin/tech/billing).
            $contactData = $this->buildContactData($params);
            if (! empty($contactData)) {
                $requestData['domain']['contacts'] = $contactData;
            }

            // TLD-specific extras (.us nexus, .ca/.eu etc.) — builder emits these
            // from client_domain_fields; forward as `tldRequirements` (HostBill
            // create_domain($...,$tld_requirements) → envelope 'tldRequirements').
            if (! empty($params['additional_fields']) && is_array($params['additional_fields'])) {
                $requestData['tldRequirements'] = $params['additional_fields'];
            }

            // Privacy/WhoisGuard: createDomain has no privacyEnabled field — privacy is a
            // separate :purchasePrivacy purchase applied after a successful registration.
            $wantsPrivacy = ! empty($params['id_protection']) || ! empty($params['privacy']);

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

            if ($result['status'] !== 'success') {
                return $result;
            }

            // Privacy is a separate purchase on name.com v4.
            if ($wantsPrivacy) {
                $this->callApi("domains/{$domain}:purchasePrivacy", 'POST', [
                    'purchasePrice' => $params['privacy_price'] ?? null,
                    'years' => $years,
                ]);
            }

            Log::info('Name.com: Domain registered', [
                'domain' => $domain,
            ]);

            return $this->successResponse(__('admin/modules.namecom_register_success'), [
                'domain' => $domain,
                'period' => $years,
                'registered_at' => now()->toISOString(),
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Register domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

    /**
     * Transfer a domain
     * POST /v4/transfers (name.com v4 CreateTransfer — separate namespace, domain in body)
     */
    public function transferDomain(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            // v4 CreateTransfer body: domainName / authCode / privacyEnabled /
            // purchasePrice / promoCode only — NO `years` field (would be ignored
            // or 400). purchasePrice is required for premium transfers.
            $requestData = [
                'domainName' => $domain,
                'authCode' => $params['epp_code'] ?? $params['auth_code'] ?? '',
            ];

            if (! empty($params['purchase_price'])) {
                $requestData['purchasePrice'] = $params['purchase_price'];
            }

            // Privacy
            if (! empty($params['id_protection']) || ! empty($params['privacy'])) {
                $requestData['privacyEnabled'] = true;
            }

            $result = $this->callApi('transfers', 'POST', $requestData);

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: Domain transfer initiated', ['domain' => $domain]);

            return $this->successResponse(__('admin/modules.namecom_transfer_success'), [
                'domain' => $domain,
                'transfer_started_at' => now()->toISOString(),
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Transfer domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

    /**
     * Renew a domain
     * POST /domains/{domain}:renew
     */
    public function renewDomain(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $years = (int) ($params['period'] ?? $params['years'] ?? 1);

            $requestData = [
                'purchasePrice' => $params['purchase_price'] ?? null,
                'years' => $years,
            ];

            $result = $this->callApi("domains/{$domain}:renew", 'POST', $requestData);

            if ($result['status'] !== 'success') {
                return $result;
            }

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

            return $this->successResponse(__('admin/modules.namecom_renew_success'), [
                'domain' => $domain,
                'period' => $years,
                'renewed_at' => now()->toISOString(),
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Renew domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

    /**
     * Set nameservers for a domain
     * POST /domains/{domain}:setNameservers (name.com v4 colon-action verb)
     */
    public function setNameservers(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

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

            $result = $this->callApi("domains/{$domain}:setNameservers", 'POST', [
                'nameservers' => array_values($nameservers),
            ]);

            if ($result['status'] !== 'success') {
                return $result;
            }

            return $this->successResponse(__('admin/modules.namecom_nameservers_updated'), [
                'domain' => $domain,
                'nameservers' => $nameservers,
                'updated_at' => now()->toISOString(),
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Set nameservers error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Get nameservers for a domain
     * GET /domains/{domain} - extract nameservers from domain details
     */
    public function getNameservers(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}", 'GET');

            if ($result['status'] !== 'success') {
                return $result;
            }

            $nameservers = $result['data']['nameservers'] ?? [];

            return $this->successResponse(__('admin/modules.namecom_nameservers_retrieved'), [
                'domain' => $domain,
                'nameservers' => array_values($nameservers),
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Get nameservers error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

    /**
     * Set contact details for a domain
     * POST /domains/{domain}:setContacts (name.com v4 colon-action verb)
     *
     * modifyContact sends admin-edited contacts under $params['groups']
     * (capitalized module-authored keys). buildContactData resolves both the
     * groups payload and the canonical PRC-969 groups via $this->contact().
     */
    public function setContactDetails(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $contactData = $this->buildContactData($params);

            $result = $this->callApi("domains/{$domain}:setContacts", 'POST', [
                'contacts' => $contactData,
            ]);

            if ($result['status'] !== 'success') {
                return $result;
            }

            return $this->successResponse(__('admin/modules.namecom_contacts_updated'), [
                'domain' => $domain,
                'updated_at' => now()->toISOString(),
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Set contacts error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Get contact details for a domain
     * GET /domains/{domain} — contacts live inside the domain object (.contacts),
     * name.com v4 has no /contacts sub-resource.
     */
    public function getContactDetails(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}", 'GET');

            if ($result['status'] !== 'success') {
                return $result;
            }

            $contacts = $result['data']['contacts'] ?? [];
            $normalized = [];

            foreach (['registrant', 'admin', 'tech', 'billing'] as $type) {
                $c = $contacts[$type] ?? [];
                $normalized[ucfirst($type)] = [
                    'firstname' => $c['firstName'] ?? '',
                    'lastname' => $c['lastName'] ?? '',
                    'company' => $c['companyName'] ?? '',
                    'address1' => $c['address1'] ?? '',
                    'address2' => $c['address2'] ?? '',
                    'city' => $c['city'] ?? '',
                    'state' => $c['state'] ?? '',
                    'postcode' => $c['zip'] ?? '',
                    'country' => $c['country'] ?? '',
                    'phone' => $c['phone'] ?? '',
                    'fax' => $c['fax'] ?? '',
                    'email' => $c['email'] ?? '',
                ];
            }

            // Return under `groups` so AbstractRegistrar::normalizeContactResponse
            // and the admin contact-edit UI round-trip can consume it (contract §8).
            return $this->successResponse(__('admin/modules.namecom_contacts_retrieved'), [
                'domain' => $domain,
                'groups' => $normalized,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Get contacts error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    // -------------------------------------------------------------------------
    //  Domain Details
    // -------------------------------------------------------------------------

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

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}", 'GET');

            if ($result['status'] !== 'success') {
                return $result;
            }

            $data = $result['data'];

            return $this->successResponse(__('admin/modules.namecom_domain_details_retrieved'), [
                'domain' => $data['domainName'] ?? $domain,
                'nameservers' => $data['nameservers'] ?? [],
                'locked' => $data['locked'] ?? false,
                'auto_renew' => $data['autorenewEnabled'] ?? false,
                'privacy' => $data['privacyEnabled'] ?? false,
                'expiry_date' => $data['expireDate'] ?? null,
                'create_date' => $data['createDate'] ?? null,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Get domain details error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

    /**
     * Get domain lock status
     * GET /domains/{domain} - check locked field
     */
    public function getDomainLock(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}", 'GET');

            if ($result['status'] !== 'success') {
                return $result;
            }

            $locked = $result['data']['locked'] ?? false;

            return $this->successResponse(__('admin/modules.namecom_lock_status_retrieved'), [
                'domain' => $domain,
                'locked' => (bool) $locked,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Get domain lock error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Update domain lock status
     * POST /domains/{domain}:lock or POST /domains/{domain}:unlock
     */
    public function updateDomainLock(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $lock = $params['lock'] ?? $params['locked'] ?? true;
            $action = $lock ? 'lock' : 'unlock';

            $result = $this->callApi("domains/{$domain}:{$action}", 'POST', []);

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: Domain lock updated', ['domain' => $domain, 'action' => $action]);

            return $this->successResponse(
                $lock
                    ? __('admin/modules.namecom_domain_locked')
                    : __('admin/modules.namecom_domain_unlocked'),
                [
                    'domain' => $domain,
                    'locked' => (bool) $lock,
                ]
            );
        } catch (\Exception $e) {
            Log::error('Name.com: Update domain lock error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

    /**
     * Get EPP/auth code for a domain
     * GET /domains/{domain}:getAuthCode
     */
    public function getEppCode(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}:getAuthCode", 'GET');

            if ($result['status'] !== 'success') {
                return $result;
            }

            $eppCode = $result['data']['authCode'] ?? '';

            return $this->successResponse(__('admin/modules.namecom_epp_code_retrieved'), [
                'domain' => $domain,
                'epp_code' => $eppCode,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Get EPP code error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

    /**
     * Check domain availability
     * POST /domains:checkAvailability
     */
    public function checkDomainAvailability(array $params)
    {
        try {
            $this->validateConfig();

            $domainNames = $params['domains'] ?? $params['domain_names'] ?? [];
            if (! is_array($domainNames)) {
                $domainNames = [$domainNames];
            }

            // Single domain check
            if (empty($domainNames) && ! empty($params['domain'])) {
                $domainNames = [$params['domain']];
            }

            if (empty($domainNames)) {
                return $this->errorResponse(__('admin/modules.namecom_domain_required'));
            }

            $result = $this->callApi('domains:checkAvailability', 'POST', [
                'domainNames' => array_values($domainNames),
            ]);

            if ($result['status'] !== 'success') {
                return $result;
            }

            $results = $result['data']['results'] ?? [];
            $availability = [];

            foreach ($results as $item) {
                // v4 CheckAvailability response has NO `premium`/`transferPrice`
                // (those only come from domains:search). Do not fabricate them.
                $availability[] = [
                    'domain' => $item['domainName'] ?? '',
                    'available' => ($item['purchasable'] ?? false) === true,
                    'purchase_price' => $item['purchasePrice'] ?? null,
                    'renewal_price' => $item['renewalPrice'] ?? null,
                ];
            }

            return $this->successResponse(__('admin/modules.namecom_availability_checked'), [
                'results' => $availability,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Check availability error', ['error' => $e->getMessage()]);

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

    /**
     * Domain name suggestions / search.
     * POST /domains:search  {keyword, tldFilter[], timeout}
     * name.com v4 DomainSearch — returns CheckAvailability results INCLUDING the
     * `premium` flag (unlike :checkAvailability).
     */
    public function checkDomainSuggestions(array $params)
    {
        try {
            $this->validateConfig();

            $keyword = $params['query'] ?? $params['keyword'] ?? $params['domain'] ?? '';
            if (empty($keyword)) {
                return $this->errorResponse(__('admin/modules.namecom_domain_required'));
            }

            $tlds = $params['tlds'] ?? $params['tldFilter'] ?? [];
            if (! is_array($tlds)) {
                $tlds = array_filter([$tlds]);
            }

            $requestData = ['keyword' => $keyword];
            if (! empty($tlds)) {
                // v4 tldFilter entries are bare TLDs without leading dot.
                $requestData['tldFilter'] = array_values(array_map(
                    static fn ($t) => ltrim((string) $t, '.'),
                    $tlds
                ));
            }
            if (! empty($params['timeout'])) {
                $requestData['timeout'] = (int) $params['timeout'];
            }

            $result = $this->callApi('domains:search', 'POST', $requestData);

            if ($result['status'] !== 'success') {
                return $result;
            }

            $suggestions = [];
            foreach (($result['data']['results'] ?? []) as $item) {
                $suggestions[] = [
                    'domain' => $item['domainName'] ?? '',
                    'available' => ($item['purchasable'] ?? false) === true,
                    'premium' => ($item['premium'] ?? false) === true,
                    'purchase_price' => $item['purchasePrice'] ?? null,
                    'renewal_price' => $item['renewalPrice'] ?? null,
                ];
            }

            return $this->successResponse(__('admin/modules.namecom_suggestions_retrieved'), [
                'suggestions' => $suggestions,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Domain suggestions error', ['error' => $e->getMessage()]);

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

    // -------------------------------------------------------------------------
    //  Child Nameservers (Vanity / Glue Records)
    // -------------------------------------------------------------------------

    /**
     * List child (vanity) nameservers for a domain.
     * GET /domains/{domain}/vanity_nameservers  (v4 ListVanityNameservers)
     */
    public function getChildNameservers(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}/vanity_nameservers", 'GET');

            if ($result['status'] !== 'success') {
                return $result;
            }

            $records = [];
            foreach (($result['data']['vanityNameservers'] ?? []) as $vns) {
                $records[] = [
                    'nameserver' => $vns['hostname'] ?? '',
                    'ips' => array_values($vns['ips'] ?? []),
                ];
            }

            return $this->successResponse(__('admin/modules.namecom_child_ns_retrieved'), [
                'domain' => $domain,
                'nameservers' => $records,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Get child nameservers error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Create a child (vanity) nameserver / glue record.
     * POST /domains/{domain}/vanity_nameservers  {hostname, ips[]}  (v4 CreateVanityNameserver)
     */
    public function addChildNameserver(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        $hostname = $params['nameserver'] ?? $params['hostname'] ?? '';
        if (empty($hostname)) {
            return $this->errorResponse(__('admin/modules.namecom_child_ns_hostname_required'));
        }

        try {
            $this->validateConfig();

            $ips = $params['ips'] ?? $params['ip'] ?? [];
            if (! is_array($ips)) {
                $ips = array_filter([$ips]);
            }

            $result = $this->callApi("domains/{$domain}/vanity_nameservers", 'POST', [
                'hostname' => $hostname,
                'ips' => array_values($ips),
            ]);

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: Child nameserver added', ['domain' => $domain]);

            return $this->successResponse(__('admin/modules.namecom_child_ns_added'), [
                'domain' => $domain,
                'nameserver' => $hostname,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Add child nameserver error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Update a child (vanity) nameserver's glue IPs.
     * PUT /domains/{domain}/vanity_nameservers/{hostname}  {ips[]}  (v4 UpdateVanityNameserver)
     */
    public function updateChildNameserver(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        $hostname = $params['nameserver'] ?? $params['hostname'] ?? '';
        if (empty($hostname)) {
            return $this->errorResponse(__('admin/modules.namecom_child_ns_hostname_required'));
        }

        try {
            $this->validateConfig();

            $ips = $params['ips'] ?? $params['ip'] ?? [];
            if (! is_array($ips)) {
                $ips = array_filter([$ips]);
            }

            $result = $this->callApi("domains/{$domain}/vanity_nameservers/{$hostname}", 'PUT', [
                'ips' => array_values($ips),
            ]);

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: Child nameserver updated', ['domain' => $domain]);

            return $this->successResponse(__('admin/modules.namecom_child_ns_updated'), [
                'domain' => $domain,
                'nameserver' => $hostname,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Update child nameserver error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Delete a child (vanity) nameserver.
     * DELETE /domains/{domain}/vanity_nameservers/{hostname}  (v4 DeleteVanityNameserver)
     */
    public function deleteChildNameserver(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        $hostname = $params['nameserver'] ?? $params['hostname'] ?? '';
        if (empty($hostname)) {
            return $this->errorResponse(__('admin/modules.namecom_child_ns_hostname_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}/vanity_nameservers/{$hostname}", 'DELETE');

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: Child nameserver deleted', ['domain' => $domain]);

            return $this->successResponse(__('admin/modules.namecom_child_ns_deleted'), [
                'domain' => $domain,
                'nameserver' => $hostname,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Delete child nameserver error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    // -------------------------------------------------------------------------
    //  Privacy (WHOIS Guard)
    // -------------------------------------------------------------------------

    /**
     * Enable/disable WHOIS privacy on an existing domain.
     * enable  → POST /domains/{domain}:enableWhoisPrivacy   (v4 EnableWhoisPrivacy)
     * disable → POST /domains/{domain}:disableWhoisPrivacy  (v4 DisableWhoisPrivacy)
     *
     * Note: enabling privacy on a domain that never purchased it may require a
     * paid :purchasePrivacy first; :enableWhoisPrivacy toggles an already-owned
     * privacy subscription. We use :purchasePrivacy when a purchase_price is
     * supplied (fresh purchase), otherwise the toggle verbs.
     */
    public function updatePrivacySettings(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $enable = ! empty($params['privacy']) || ! empty($params['id_protection']) || ! empty($params['enable']);

            if ($enable) {
                if (! empty($params['purchase_price'])) {
                    $years = (int) ($params['period'] ?? $params['years'] ?? 1);
                    $result = $this->callApi("domains/{$domain}:purchasePrivacy", 'POST', [
                        'purchasePrice' => $params['purchase_price'],
                        'years' => $years,
                    ]);
                } else {
                    $result = $this->callApi("domains/{$domain}:enableWhoisPrivacy", 'POST', []);
                }
            } else {
                $result = $this->callApi("domains/{$domain}:disableWhoisPrivacy", 'POST', []);
            }

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: Privacy updated', ['domain' => $domain, 'enabled' => $enable]);

            return $this->successResponse(
                $enable
                    ? __('admin/modules.namecom_privacy_enabled')
                    : __('admin/modules.namecom_privacy_disabled'),
                [
                    'domain' => $domain,
                    'privacy' => $enable,
                ]
            );
        } catch (\Exception $e) {
            Log::error('Name.com: Update privacy error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    // -------------------------------------------------------------------------
    //  Sync (Cron)
    // -------------------------------------------------------------------------

    /**
     * Synchronise domain state (expiry/status/NS/lock/auto-renew) from registrar.
     * GET /domains/{domain}  (same source as getDomainDetails; HostBill synchInfo).
     */
    public function syncDomain(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}", 'GET');

            if ($result['status'] !== 'success') {
                return $result;
            }

            $data = $result['data'];
            $expire = $data['expireDate'] ?? null;
            $status = ($expire && strtotime($expire) < time()) ? 'Expired' : 'Active';

            return $this->successResponse(__('admin/modules.namecom_sync_success'), [
                'domain' => $data['domainName'] ?? $domain,
                'status' => $status,
                'expiry_date' => $expire,
                'nameservers' => array_values($data['nameservers'] ?? []),
                'locked' => (bool) ($data['locked'] ?? false),
                'auto_renew' => (bool) ($data['autorenewEnabled'] ?? false),
                'privacy' => (bool) ($data['privacyEnabled'] ?? false),
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Sync domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Poll transfer progress.
     * GET /transfers/{domain}  (v4 GetTransfer) — status drives completion.
     * A missing transfer record + a resolvable domain object means the transfer
     * already completed, so we cross-check GET /domains/{domain}.
     */
    public function syncDomainTransfer(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("transfers/{$domain}", 'GET');

            if ($result['status'] === 'success') {
                $raw = strtolower((string) ($result['data']['status'] ?? ''));

                if (str_contains($raw, 'complet')) {
                    $state = 'completed';
                } elseif (str_contains($raw, 'cancel') || str_contains($raw, 'fail') || str_contains($raw, 'reject')) {
                    $state = 'failed';
                } else {
                    $state = 'pending';
                }

                return $this->successResponse(__('admin/modules.namecom_transfer_sync_success'), [
                    'domain' => $domain,
                    'transfer_status' => $state,
                    'raw_status' => $result['data']['status'] ?? null,
                ]);
            }

            // No pending transfer record — cross-check the domain object: if it
            // resolves in the account, the transfer has completed.
            $domainCheck = $this->callApi("domains/{$domain}", 'GET');
            if ($domainCheck['status'] === 'success' && ! empty($domainCheck['data']['domainName'])) {
                return $this->successResponse(__('admin/modules.namecom_transfer_sync_success'), [
                    'domain' => $domain,
                    'transfer_status' => 'completed',
                    'expiry_date' => $domainCheck['data']['expireDate'] ?? null,
                ]);
            }

            return $result;
        } catch (\Exception $e) {
            Log::error('Name.com: Sync transfer error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    // -------------------------------------------------------------------------
    //  DNS Records
    // -------------------------------------------------------------------------

    /**
     * Get DNS records for a domain
     * GET /domains/{domain}/records
     */
    public function getDNSRecords(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}/records", 'GET');

            if ($result['status'] !== 'success') {
                return $result;
            }

            $rawRecords = $result['data']['records'] ?? [];
            $records = [];

            foreach ($rawRecords as $record) {
                $records[] = [
                    'id' => $record['id'] ?? null,
                    'host' => $record['host'] ?? '',
                    'fqdn' => $record['fqdn'] ?? '',
                    'type' => $record['type'] ?? '',
                    'answer' => $record['answer'] ?? '',
                    'ttl' => $record['ttl'] ?? 300,
                    'priority' => $record['priority'] ?? null,
                ];
            }

            return $this->successResponse(__('admin/modules.namecom_dns_records_retrieved'), [
                'domain' => $domain,
                'records' => $records,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Get DNS records error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Add a DNS record
     * POST /domains/{domain}/records
     */
    public function addDNSRecord(array $params)
    {
        $domain = $params['domain'] ?? '';
        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        try {
            $this->validateConfig();

            $recordData = [
                'host' => $params['host'] ?? '',
                'type' => $params['type'] ?? 'A',
                'answer' => $params['answer'] ?? $params['value'] ?? '',
                'ttl' => (int) ($params['ttl'] ?? 300),
            ];

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

            $result = $this->callApi("domains/{$domain}/records", 'POST', $recordData);

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: DNS record added', ['domain' => $domain, 'type' => $recordData['type']]);

            return $this->successResponse(__('admin/modules.namecom_dns_record_added'), [
                'domain' => $domain,
                'record_id' => $result['data']['id'] ?? null,
                'type' => $recordData['type'],
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Add DNS record error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Update a DNS record
     * PUT /domains/{domain}/records/{id}
     */
    public function updateDNSRecord(array $params)
    {
        $domain = $params['domain'] ?? '';
        $recordId = $params['record_id'] ?? $params['id'] ?? '';

        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        if (empty($recordId)) {
            return $this->errorResponse(__('admin/modules.namecom_record_id_required'));
        }

        try {
            $this->validateConfig();

            $recordData = [];

            if (isset($params['host'])) {
                $recordData['host'] = $params['host'];
            }
            if (isset($params['type'])) {
                $recordData['type'] = $params['type'];
            }
            if (isset($params['answer']) || isset($params['value'])) {
                $recordData['answer'] = $params['answer'] ?? $params['value'];
            }
            if (isset($params['ttl'])) {
                $recordData['ttl'] = (int) $params['ttl'];
            }
            if (isset($params['priority'])) {
                $recordData['priority'] = (int) $params['priority'];
            }

            $result = $this->callApi("domains/{$domain}/records/{$recordId}", 'PUT', $recordData);

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: DNS record updated', ['domain' => $domain, 'record_id' => $recordId]);

            return $this->successResponse(__('admin/modules.namecom_dns_record_updated'), [
                'domain' => $domain,
                'record_id' => $recordId,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Update DNS record error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    /**
     * Delete a DNS record
     * DELETE /domains/{domain}/records/{id}
     */
    public function deleteDNSRecord(array $params)
    {
        $domain = $params['domain'] ?? '';
        $recordId = $params['record_id'] ?? $params['id'] ?? '';

        if (empty($domain)) {
            return $this->errorResponse(__('admin/modules.namecom_domain_required'));
        }

        if (empty($recordId)) {
            return $this->errorResponse(__('admin/modules.namecom_record_id_required'));
        }

        try {
            $this->validateConfig();

            $result = $this->callApi("domains/{$domain}/records/{$recordId}", 'DELETE');

            if ($result['status'] !== 'success') {
                return $result;
            }

            Log::info('Name.com: DNS record deleted', ['domain' => $domain, 'record_id' => $recordId]);

            return $this->successResponse(__('admin/modules.namecom_dns_record_deleted'), [
                'domain' => $domain,
                'record_id' => $recordId,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Delete DNS record error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

    // -------------------------------------------------------------------------
    //  Domain Pricings
    // -------------------------------------------------------------------------

    /**
     * Get domain pricing.
     *
     * name.com v4 has no `domains:getPricing` verb; per-domain register/renew
     * pricing is returned by checkAvailability (purchasePrice/renewalPrice).
     * Requires concrete domain names in $params (no account-wide TLD price list).
     */
    public function getDomainPricings(array $params)
    {
        try {
            $this->validateConfig();

            $domainNames = $params['domains'] ?? $params['domain_names'] ?? [];
            if (! is_array($domainNames)) {
                $domainNames = [$domainNames];
            }
            if (empty($domainNames) && ! empty($params['domain'])) {
                $domainNames = [$params['domain']];
            }

            if (empty($domainNames)) {
                return $this->errorResponse(__('admin/modules.namecom_domain_required'));
            }

            $result = $this->callApi('domains:checkAvailability', 'POST', [
                'domainNames' => array_values($domainNames),
            ]);

            if ($result['status'] !== 'success') {
                return $result;
            }

            $pricings = [];
            foreach (($result['data']['results'] ?? []) as $item) {
                $name = $item['domainName'] ?? '';
                $pricings[] = [
                    'tld' => $name !== '' && str_contains($name, '.') ? substr($name, strpos($name, '.') + 1) : $name,
                    'domain' => $name,
                    'register' => $item['purchasePrice'] ?? null,
                    'renew' => $item['renewalPrice'] ?? null,
                    'transfer' => $item['transferPrice'] ?? null,
                ];
            }

            return $this->successResponse(__('admin/modules.namecom_pricings_retrieved'), [
                'pricings' => $pricings,
            ]);
        } catch (\Exception $e) {
            Log::error('Name.com: Get pricings error', ['error' => $e->getMessage()]);

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

    // -------------------------------------------------------------------------
    //  Contact Data Helper
    // -------------------------------------------------------------------------

    /**
     * Build Name.com `contacts.{registrant,admin,tech,billing}` structure from
     * the full registrar param set.
     *
     * PRC-969: never reads a single `$params['contact']` (which no caller emits).
     * Precedence per group:
     *   1. `$params['groups'][Ucfirst]`  — modifyContact admin-edited payload
     *      (module-authored keys: firstname/lastname/company/postcode/…)
     *   2. `$this->contact($params, $group)` — canonical builder groups + client_* fallback
     *
     * name.com shape (verified vs HostBill namecom_v2 + name.com v4 docs):
     *   firstName, lastName, companyName, address1, address2, city, state, zip,
     *   country, phone, fax, email
     */
    protected function buildContactData(array $params): array
    {
        $groups = (isset($params['groups']) && is_array($params['groups'])) ? $params['groups'] : [];

        $out = [];
        foreach (['registrant', 'admin', 'tech', 'billing'] as $group) {
            $edited = $groups[ucfirst($group)] ?? $groups[$group] ?? null;

            if (is_array($edited) && ! empty($edited)) {
                // modifyContact admin-edited group (module-authored key names).
                $out[$group] = $this->mapContactShape([
                    'first_name'   => $edited['firstname'] ?? $edited['first_name'] ?? '',
                    'last_name'    => $edited['lastname'] ?? $edited['last_name'] ?? '',
                    'company'      => $edited['company'] ?? $edited['companyname'] ?? '',
                    'email'        => $edited['email'] ?? '',
                    'phone'        => $edited['phone'] ?? $edited['phonenumber'] ?? '',
                    'address1'     => $edited['address1'] ?? '',
                    'address2'     => $edited['address2'] ?? '',
                    'city'         => $edited['city'] ?? '',
                    'state'        => $edited['state'] ?? '',
                    'postcode'     => $edited['postcode'] ?? $edited['zip'] ?? '',
                    'country_code' => $edited['country'] ?? $edited['country_code'] ?? '',
                    'fax'          => $edited['fax'] ?? '',
                ]);

                continue;
            }

            // Canonical PRC-969 group (builder contacts + client_* fallback).
            $out[$group] = $this->mapContactShape($this->contact($params, $group));
        }

        // Drop entirely-empty groups so we never transmit blank WHOIS contacts.
        $out = array_filter($out, static function (array $c): bool {
            return $c['firstName'] !== '' || $c['lastName'] !== '' || $c['email'] !== '';
        });

        return $out;
    }

    /**
     * Map a canonical contact (first_name/last_name/company/email/phone/address1/
     * address2/city/state/postcode/country_code[/fax]) into name.com's contact shape.
     */
    protected function mapContactShape(array $c): array
    {
        return [
            'firstName' => $c['first_name'] ?? '',
            'lastName' => $c['last_name'] ?? '',
            'companyName' => $c['company'] ?? '',
            'address1' => $c['address1'] ?? '',
            'address2' => $c['address2'] ?? '',
            'city' => $c['city'] ?? '',
            'state' => $c['state'] ?? '',
            'zip' => $c['postcode'] ?? '',
            'country' => $c['country_code'] ?? '',
            'phone' => $c['phone'] ?? '',
            'fax' => $c['fax'] ?? '',
            'email' => $c['email'] ?? '',
        ];
    }
}
