<?php

namespace App\Modules\Registrar\DomainNameApi;

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

/**
 * DomainNameApi (DNA) registrar module — THIN ADAPTER over the official
 * whmcs-dna v3.0.5 vendor library (WHMCS/WiseCP pattern).
 *
 * The transport, request building and response parsing all live in the
 * vendored library under ./lib (facade `\DomainNameApi\DomainNameAPI_PHPLibrary`).
 * The facade AUTO-SELECTS the transport from the username format:
 *   - UUID username → REST (DNARest)
 *   - normal username → SOAP (DNASoap)
 * This is the whole reason for the rewrite: the previous inline-SOAP module
 * broke UUID/REST accounts. Do NOT reintroduce transport logic here.
 *
 * This class is glue only:
 *   1. build the library's contact input from the canonical PRC-969 contact
 *      groups (buildLibraryContacts),
 *   2. delegate to the facade method,
 *   3. adapt the library's `['result'=>'OK'|'ERROR','data'=>...,'error'=>...]`
 *      envelope to procp's `['status','message','data']` (adaptResult).
 *
 * PRC-49: never queries ClientDomain/Client/Tld; TLD via explode(); never
 * logs credentials (username/password/module_config).
 */
class DomainNameApi extends AbstractRegistrar
{
    /**
     * Library success/error sentinels (mirror of the vendored facade's
     * `$RESULT_OK`/`$RESULT_ERROR`). Kept as local constants so response
     * adaptation never has to load the library class — the adapter can be
     * unit-tested with a mock facade without requiring lib/dna.php.
     */
    protected const RESULT_OK = 'OK';

    protected const RESULT_ERROR = 'ERROR';

    /**
     * Memoized facade instance (per-module-instance). Reset is not needed;
     * a module instance always carries one config.
     */
    protected ?object $libraryInstance = null;

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

    // -------------------------------------------------------------------------
    //  Config & Validation
    // -------------------------------------------------------------------------

    public function getConfigFields(): array
    {
        return [
            'username' => [
                'type' => 'text',
                'label' => __('admin/modules.domainnameapi_field_username_label'),
                'required' => true,
                'description' => __('admin/modules.domainnameapi_field_username_description'),
                'placeholder' => 'reseller@example.com',
                'value' => $this->config['username'] ?? '',
            ],
            'password' => [
                'type' => 'password',
                'label' => __('admin/modules.domainnameapi_field_password_label'),
                'required' => true,
                'description' => __('admin/modules.domainnameapi_field_password_description'),
                'placeholder' => '********',
                'value' => $this->config['password'] ?? '',
            ],
            'test_mode' => [
                'type' => 'checkbox',
                'label' => __('admin/modules.domainnameapi_field_test_mode_label'),
                'required' => false,
                'description' => __('admin/modules.domainnameapi_field_test_mode_description'),
                'value' => $this->config['test_mode'] ?? false,
            ],
        ];
    }

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

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

        return true;
    }

    // -------------------------------------------------------------------------
    //  Module Meta
    // -------------------------------------------------------------------------

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

        return array_merge($defaultInfo, [
            'label' => __('admin/modules.domainnameapi_module_label'),
            'description' => __('admin/modules.domainnameapi_module_description'),
            'features' => [
                'domain_register' => true,
                'domain_transfer' => true,
                'domain_renew' => true,
                'domain_nameservers' => true,
                'domain_contacts' => true,
                'domain_epp' => true,
                'domain_privacy' => true,
                'domain_lock' => true,
                'domain_availability' => true,
                'domain_pricing' => true,
            ],
        ]);
    }

    // -------------------------------------------------------------------------
    //  Library seam (testable) — construct/memoize the vendor facade
    // -------------------------------------------------------------------------

    /**
     * Load + instantiate the vendored DNA facade. The facade auto-selects REST
     * (UUID username) or SOAP (normal username). Wrapped in a seam so tests can
     * inject a mock without touching the network.
     *
     * Returns `object` (not the concrete facade type) so tests can inject a
     * duck-typed mock through this seam.
     *
     * @throws \Throwable when the library cannot be constructed (bad creds,
     *                    ext-soap missing for a SOAP account, etc.)
     */
    protected function library(): object
    {
        if ($this->libraryInstance instanceof \DomainNameApi\DomainNameAPI_PHPLibrary) {
            return $this->libraryInstance;
        }

        require_once __DIR__.'/lib/dna.php';

        $this->libraryInstance = new \DomainNameApi\DomainNameAPI_PHPLibrary(
            (string) ($this->config['username'] ?? ''),
            (string) ($this->config['password'] ?? ''),
            $this->isTestMode()
        );

        // GÜVENLİK: vendored lib hata durumunda request_data'yı (UserName/Password
        // dahil) DNA'nın harici Sentry DSN'ine POST ediyor. Müşteri kimlik
        // bilgilerinin 3rd-party'ye sızmaması için telemetriyi kapat (PRC-49 —
        // credential dışarı verilmez).
        if (method_exists($this->libraryInstance, 'setErrorReportingEnabled')) {
            $this->libraryInstance->setErrorReportingEnabled(false);
        }

        return $this->libraryInstance;
    }

    /**
     * Resolve the facade, converting any construction failure into a procp
     * error response (graceful — never fatal). Returns the facade on success,
     * or an error-envelope array on failure. Callers must check for the array.
     *
     * @return \DomainNameApi\DomainNameAPI_PHPLibrary|array
     */
    protected function resolveLibrary()
    {
        try {
            $this->validateConfig();

            return $this->library();
        } catch (\Throwable $e) {
            Log::error('DomainNameApi: library init failed', ['error' => $e->getMessage()]);

            return $this->errorResponse(
                __('admin/modules.domainnameapi_api_error', ['error' => $e->getMessage()])
            );
        }
    }

    // -------------------------------------------------------------------------
    //  Response adaptation (pure, testable)
    // -------------------------------------------------------------------------

    /**
     * Adapt a library envelope to procp's `['status','message','data']`.
     *
     * Library shape:
     *   OK    → ['result' => 'OK', 'data' => [...]]
     *   ERROR → ['result' => 'ERROR', 'error' => ['Code','Message','Details']]
     *           (`error` may also be a plain string for some paths)
     *
     * @param  array  $result   Raw library envelope
     * @param  string $message  Success message (procp translation)
     * @param  array  $extra    Extra data merged into the success payload
     */
    protected function adaptResult(array $result, string $message = '', array $extra = []): array
    {
        if (($result['result'] ?? null) === self::RESULT_OK) {
            $data = is_array($result['data'] ?? null) ? $result['data'] : [];

            return $this->successResponse($message, array_merge($data, $extra));
        }

        return $this->errorResponse($this->extractError($result));
    }

    /**
     * Pull a human-readable error message out of a library error envelope.
     */
    protected function extractError(array $result): string
    {
        $error = $result['error'] ?? null;

        if (is_array($error)) {
            return (string) ($error['Message']
                ?? $error['Details']
                ?? $error['Code']
                ?? __('admin/modules.domainnameapi_unknown_error'));
        }

        if (is_string($error) && $error !== '') {
            return $error;
        }

        return __('admin/modules.domainnameapi_unknown_error');
    }

    // -------------------------------------------------------------------------
    //  Contact input builder (pure, testable) — PRC-969
    // -------------------------------------------------------------------------

    /**
     * Build the library's `$contacts` input from the canonical PRC-969 contact
     * groups. The library expects SOAP-style keys per group:
     *   ['Registrant'=>[...],'Administrative'=>[...],'Technical'=>[...],'Billing'=>[...]]
     * where each group carries FirstName/LastName/Company/EMail/AddressLine1/
     * AddressLine2/State/City/Country/Phone/PhoneCountryCode/ZipCode/Fax/
     * FaxCountryCode/Type/Status. The library's parseContact re-maps these to
     * its transport shape and re-splits phone as needed, so we pass phone as-is.
     *
     * @return array<string,array<string,string>>
     */
    protected function buildLibraryContacts(array $params): array
    {
        return [
            'Registrant' => $this->mapCanonicalContact($this->contact($params, 'registrant')),
            'Administrative' => $this->mapCanonicalContact($this->contact($params, 'admin')),
            'Technical' => $this->mapCanonicalContact($this->contact($params, 'tech')),
            'Billing' => $this->mapCanonicalContact($this->contact($params, 'billing')),
        ];
    }

    /**
     * Map a canonical contact (11 keys) to the library's SOAP-style group shape.
     */
    protected function mapCanonicalContact(array $c): array
    {
        return [
            'FirstName' => (string) ($c['first_name'] ?? ''),
            'LastName' => (string) ($c['last_name'] ?? ''),
            'Company' => (string) ($c['company'] ?? ''),
            'EMail' => (string) ($c['email'] ?? ''),
            'AddressLine1' => (string) ($c['address1'] ?? ''),
            'AddressLine2' => (string) ($c['address2'] ?? ''),
            'State' => (string) ($c['state'] ?? ''),
            'City' => (string) ($c['city'] ?? ''),
            'Country' => (string) ($c['country_code'] ?? ''),
            'ZipCode' => (string) ($c['postcode'] ?? ''),
            // The library re-splits phone/cc in validateContact; pass as-is.
            'Phone' => (string) ($c['phone'] ?? ''),
            'PhoneCountryCode' => '',
            'Fax' => '',
            'FaxCountryCode' => '',
            'Type' => 'Contact',
            'Status' => '',
        ];
    }

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $period = (int) ($params['period'] ?? 1);
            $contacts = $this->buildLibraryContacts($params);
            $nameServers = $this->cleanNameservers($params['nameservers'] ?? []);
            $privacyLock = ! empty($params['id_protection']);

            // PRC-49: TLD-özel ekstra alanları (.tr TRABIS, .uk/.fr) builder
            // client_domain_fields'ten $params['additional_fields'] olarak emit
            // eder; forward EDİLMEZSE vendored lib (DNASoap ~1123) SAHTE TRABIS
            // placeholder (fake citizen ID) enjekte eder. REST/SOAP lib bunu
            // object'e çevirir (DNARest ~1157 tldAttributes).
            $additional = is_array($params['additional_fields'] ?? null)
                ? $params['additional_fields']
                : [];

            $result = $lib->registerWithContactInfo(
                $domain,
                $period,
                $contacts,
                $nameServers,
                true,          // eppLock
                $privacyLock,
                $additional    // additionalAttributes (TLD-specific extras)
            );

            if (($result['result'] ?? null) === self::RESULT_OK) {
                Log::info('DomainNameApi: Domain registered', ['domain' => $domain]);
                $data = $result['data'] ?? [];

                return $this->successResponse(__('admin/modules.domainnameapi_register_success'), [
                    'domain' => $domain,
                    'period' => $period,
                    'registration_date' => $data['Dates']['Start'] ?? now()->toISOString(),
                    'expiry_date' => $data['Dates']['Expiration'] ?? '',
                    'order_id' => $data['ID'] ?? null,
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            Log::error('DomainNameApi: Register domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $authCode = (string) ($params['epp_code'] ?? $params['authcode'] ?? '');
            $period = (int) ($params['period'] ?? 1);

            $result = $lib->transfer($domain, $authCode, $period);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                Log::info('DomainNameApi: Domain transfer initiated', ['domain' => $domain]);
                $data = $result['data'] ?? [];

                return $this->successResponse(__('admin/modules.domainnameapi_transfer_success'), [
                    'domain' => $domain,
                    'transfer_id' => $data['ID'] ?? null,
                    'transfer_status' => $data['Status'] ?? 'pending',
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            Log::error('DomainNameApi: Transfer domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

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

            $result = $lib->renew($domain, $period);

            return $this->adaptResult($result, __('admin/modules.domainnameapi_renew_success'), [
                'domain' => $domain,
                'period' => $period,
                'expiry_date' => $result['data']['ExpirationDate'] ?? '',
            ]);
        } catch (\Throwable $e) {
            Log::error('DomainNameApi: Renew domain error', ['domain' => $domain, 'error' => $e->getMessage()]);

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

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->getDetails($domain);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $data = $result['data'] ?? [];

                return $this->successResponse(__('admin/modules.domainnameapi_nameservers_retrieved'), [
                    'domain' => $domain,
                    'nameservers' => $this->extractNameservers($data),
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $nameservers = $this->cleanNameservers($params['nameservers'] ?? []);

            $result = $lib->modifyNameServer($domain, $nameservers);

            return $this->adaptResult($result, __('admin/modules.domainnameapi_nameservers_updated'), [
                'domain' => $domain,
                'nameservers' => $nameservers,
                'updated_at' => now()->toISOString(),
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Contacts
    // -------------------------------------------------------------------------

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->getContacts($domain);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $contacts = $result['data']['contacts'] ?? [];
                $groups = [];

                foreach ($contacts as $type => $contactData) {
                    if (is_array($contactData) && ! empty($contactData)) {
                        $groups[$type] = $this->normalizeLibraryContact($contactData);
                    }
                }

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

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $contacts = $this->buildLibraryContacts($params);

            $result = $lib->saveContacts($domain, $contacts);

            return $this->adaptResult($result, __('admin/modules.domainnameapi_contacts_updated'), [
                'domain' => $domain,
                'updated_at' => now()->toISOString(),
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->getDetails($domain);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $data = $result['data'] ?? [];

                return $this->successResponse(__('admin/modules.domainnameapi_domain_info_retrieved'), [
                    'domain' => $domain,
                    'status' => $this->normalizeDomainStatus($data['Status'] ?? ''),
                    'created_at' => $data['Dates']['Start'] ?? '',
                    'expires_at' => $data['Dates']['Expiration'] ?? '',
                    'locked' => $this->parseBool($data['LockStatus'] ?? false),
                    'privacy_enabled' => $this->parseBool($data['PrivacyProtectionStatus'] ?? false),
                    'nameservers' => $this->extractNameservers($data),
                    'auth_code' => $data['AuthCode'] ?? '',
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->getDetails($domain);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $data = $result['data'] ?? [];

                return $this->successResponse(__('admin/modules.domainnameapi_lock_status_retrieved'), [
                    'domain' => $domain,
                    'locked' => $this->parseBool($data['LockStatus'] ?? false),
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $lockEnabled = (bool) ($params['lock_status'] ?? $params['locked'] ?? true);

            $result = $lockEnabled
                ? $lib->enableTheftProtectionLock($domain)
                : $lib->disableTheftProtectionLock($domain);

            return $this->adaptResult($result, __('admin/modules.domainnameapi_lock_updated'), [
                'domain' => $domain,
                'locked' => $lockEnabled,
                'updated_at' => now()->toISOString(),
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->getDetails($domain);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $eppCode = (string) ($result['data']['AuthCode'] ?? '');

                if ($eppCode === '') {
                    return $this->errorResponse(__('admin/modules.domainnameapi_epp_not_found'));
                }

                return $this->successResponse(__('admin/modules.domainnameapi_epp_retrieved'), [
                    'domain' => $domain,
                    'epp_code' => $eppCode,
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->getDetails($domain);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $data = $result['data'] ?? [];

                return $this->successResponse(__('admin/modules.domainnameapi_privacy_retrieved'), [
                    'domain' => $domain,
                    'privacy_enabled' => $this->parseBool($data['PrivacyProtectionStatus'] ?? false),
                    'available' => true,
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $privacyEnabled = (bool) ($params['privacy_enabled'] ?? true);

            $result = $lib->modifyPrivacyProtectionStatus($domain, $privacyEnabled);

            return $this->adaptResult($result, __('admin/modules.domainnameapi_privacy_updated'), [
                'domain' => $domain,
                'privacy_enabled' => $privacyEnabled,
                'updated_at' => now()->toISOString(),
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $tlds = $params['tlds'] ?? [];
            $sld = (string) ($params['sld'] ?? explode('.', $domain, 2)[0]);
            $period = (int) ($params['period'] ?? 1);

            if (! empty($tlds)) {
                $slds = [$sld];
                $tldList = array_map(fn ($t) => ltrim((string) $t, '.'), (array) $tlds);
            } else {
                $slds = [$sld];
                $tld = explode('.', $domain, 2)[1] ?? '';
                $tldList = [ltrim($tld, '.')];
            }

            // Library returns a plain list of availability rows (or an error
            // envelope). Each row: TLD/DomainName/Status/Command/Period/...
            $rows = $lib->checkAvailability($slds, $tldList, 1, 'create');

            if (isset($rows['result']) && $rows['result'] !== self::RESULT_OK) {
                return $this->errorResponse($this->extractError($rows));
            }

            $items = is_array($rows) ? array_values(array_filter($rows, 'is_array')) : [];

            if (empty($tlds)) {
                $first = $items[0] ?? [];
                $available = strtolower((string) ($first['Status'] ?? '')) === 'available';

                return $this->successResponse(__('admin/modules.domainnameapi_availability_checked'), [
                    'domain' => $domain,
                    'available' => $available,
                    'checked_at' => now()->toISOString(),
                ]);
            }

            $results = [];
            foreach ($items as $item) {
                $itemDomain = trim(($item['DomainName'] ?? '').'.'.($item['TLD'] ?? ''), '.');
                $results[] = [
                    'domain' => $itemDomain !== '' ? $itemDomain : ($item['DomainName'] ?? ''),
                    'available' => strtolower((string) ($item['Status'] ?? '')) === 'available',
                ];
            }

            return $this->successResponse(__('admin/modules.domainnameapi_availability_checked'), [
                'results' => $results,
                'checked_at' => now()->toISOString(),
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    /**
     * DNA vendored lib'inde domain öneri/suggestion endpoint'i YOKTUR (lib
     * genelinde 'suggest' grep=0). Sahte-success döndürmek yerine net bir
     * "desteklenmiyor" hatası döndürülür; getModuleInfo domain_suggestions
     * feature'ını deklare etmez, çağıran taraf 500 yerine temiz sonuç alır.
     */
    public function checkDomainSuggestions(array $params)
    {
        return $this->errorResponse(__('admin/modules.domainnameapi_suggestions_unsupported'));
    }

    // -------------------------------------------------------------------------
    //  Domain Pricing (TLD List)
    // -------------------------------------------------------------------------

    public function getDomainPricings(array $params)
    {
        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->getTldList(1000);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $pricings = [];
                foreach (($result['data'] ?? []) as $tld) {
                    if (! is_array($tld)) {
                        continue;
                    }
                    $parsed = $this->parseTldPricing($tld);
                    if ($parsed !== null) {
                        $pricings[$parsed['tld']] = $parsed;
                    }
                }

                return $this->successResponse(__('admin/modules.domainnameapi_pricings_retrieved'), [
                    'pricings' => $pricings,
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Sync
    // -------------------------------------------------------------------------

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->syncFromRegistry($domain);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $data = $result['data'] ?? [];

                return $this->successResponse(__('admin/modules.domainnameapi_domain_info_retrieved'), [
                    'domain' => $domain,
                    'status' => $this->normalizeDomainStatus($data['Status'] ?? ''),
                    'expires_at' => $data['Dates']['Expiration'] ?? '',
                    'locked' => $this->parseBool($data['LockStatus'] ?? false),
                    'privacy_enabled' => $this->parseBool($data['PrivacyProtectionStatus'] ?? false),
                    'nameservers' => $this->extractNameservers($data),
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            // Bir bekleyen transferin TAMAMLANIP tamamlanmadığını `checkTransfer`
            // (transfer-uygunluk kontrolü) DÖNMEZ — o yalnız "bu domain
            // transfer edilebilir mi" der. İlerlemeyi anlamanın güvenilir yolu
            // domainin hesabımıza düşüp aktif olup olmadığını sorgulamaktır
            // (Onlinenic ile aynı sözleşme: hesapta aktif → completed).
            $result = $lib->syncFromRegistry($domain);

            // SyncDomainTransfersCommand kanonik zarf bekler:
            //   status:'success' + data.transfer_status ∈ completed|pending|failed
            //   (completed'da expires_at push edilir). Availability booleanları
            //   (transferable/completed) caller tarafından okunmuyor.
            if (($result['result'] ?? null) === self::RESULT_OK) {
                $data = $result['data'] ?? [];
                $status = $this->normalizeDomainStatus((string) ($data['Status'] ?? ''));
                $isCompleted = $status === 'active';

                return $this->successResponse(__('admin/modules.domainnameapi_transfer_success'), [
                    'domain' => $domain,
                    'transfer_status' => $isCompleted ? 'completed' : 'pending',
                    'transfer_completed' => $isCompleted,
                    'expires_at' => (string) ($data['Dates']['Expiration'] ?? ''),
                ]);
            }

            // Domain henüz hesaba düşmemiş / sorgu başarısız → transfer HÂLÂ
            // sürüyor demektir; başarısız lookup transferin REDDEDİLDİĞİNİ
            // kanıtlamaz. Fail-closed: 'failed' değil 'pending' dön (caller
            // domaini pending_transfer'da bırakır, bir sonraki cron tekrar dener).
            return $this->successResponse(__('admin/modules.domainnameapi_transfer_success'), [
                'domain' => $domain,
                'transfer_status' => 'pending',
                'transfer_completed' => false,
                'expires_at' => '',
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Child Nameservers
    // -------------------------------------------------------------------------

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

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            // Child NS verisi getDetails içinde `data.ChildNameServers` olarak
            // gelir: her satır ['ns'=>..,'ip'=>..] (REST DNARest ~1279; SOAP
            // DNASoap ~1602). UI `data.nameservers` listesinde hostname+ip bekler.
            $result = $lib->getDetails($domain);

            if (($result['result'] ?? null) === self::RESULT_OK) {
                $rows = $result['data']['ChildNameServers'] ?? [];
                $nameservers = [];

                if (is_array($rows)) {
                    $i = 0;
                    foreach ($rows as $row) {
                        if (! is_array($row)) {
                            continue;
                        }
                        $host = (string) ($row['ns'] ?? $row['nameserver'] ?? '');
                        if ($host === '') {
                            continue;
                        }
                        $nameservers[] = [
                            'id' => (string) (++$i),
                            'hostname' => $host,
                            'nameserver' => $host,
                            'ip' => (string) ($row['ip'] ?? ''),
                        ];
                    }
                }

                return $this->successResponse(__('admin/modules.domainnameapi_nameservers_retrieved'), [
                    'domain' => $domain,
                    'nameservers' => $nameservers,
                ]);
            }

            return $this->errorResponse($this->extractError($result));
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    public function addChildNameserver(array $params)
    {
        $domain = (string) ($params['domain'] ?? '');
        $nameServer = (string) ($params['nameserver'] ?? '');
        $ip = (string) ($params['ip'] ?? $params['ipaddress'] ?? '');
        if ($domain === '' || $nameServer === '') {
            return $this->errorResponse(__('admin/modules.domainnameapi_domain_required'));
        }

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->addChildNameServer($domain, $nameServer, $ip);

            return $this->adaptResult($result, __('admin/modules.domainnameapi_nameservers_updated'), [
                'domain' => $domain,
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    public function updateChildNameserver(array $params)
    {
        $domain = (string) ($params['domain'] ?? '');
        $nameServer = (string) ($params['nameserver'] ?? '');
        $ip = (string) ($params['ip'] ?? $params['ipaddress'] ?? '');
        if ($domain === '' || $nameServer === '') {
            return $this->errorResponse(__('admin/modules.domainnameapi_domain_required'));
        }

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->modifyChildNameServer($domain, $nameServer, $ip);

            return $this->adaptResult($result, __('admin/modules.domainnameapi_nameservers_updated'), [
                'domain' => $domain,
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    public function deleteChildNameserver(array $params)
    {
        $domain = (string) ($params['domain'] ?? '');
        $nameServer = (string) ($params['nameserver'] ?? '');
        if ($domain === '' || $nameServer === '') {
            return $this->errorResponse(__('admin/modules.domainnameapi_domain_required'));
        }

        $lib = $this->resolveLibrary();
        if (is_array($lib)) {
            return $lib;
        }

        try {
            $result = $lib->deleteChildNameServer($domain, $nameServer);

            return $this->adaptResult($result, __('admin/modules.domainnameapi_nameservers_updated'), [
                'domain' => $domain,
            ]);
        } catch (\Throwable $e) {
            return $this->errorResponse($e->getMessage());
        }
    }

    // -------------------------------------------------------------------------
    //  Internal Helpers (pure — no network)
    // -------------------------------------------------------------------------

    /**
     * Normalize a nameserver list to a plain indexed array of non-empty strings.
     */
    protected function cleanNameservers(array $nameservers): array
    {
        $clean = [];
        foreach ($nameservers as $ns) {
            if (is_string($ns) && trim($ns) !== '') {
                $clean[] = trim($ns);
            }
        }

        return array_values($clean);
    }

    /**
     * Normalize a library contact (parseContactInfo shape: nested Address/Phone)
     * to procp's flat contact format.
     */
    protected function normalizeLibraryContact(array $c): array
    {
        return [
            'firstname' => $c['FirstName'] ?? '',
            'lastname' => $c['LastName'] ?? '',
            'company' => $c['Company'] ?? '',
            'email' => $c['EMail'] ?? ($c['Email'] ?? ''),
            'address1' => $c['Address']['Line1'] ?? ($c['AddressLine1'] ?? ''),
            'address2' => $c['Address']['Line2'] ?? ($c['AddressLine2'] ?? ''),
            'city' => $c['Address']['City'] ?? ($c['City'] ?? ''),
            'state' => $c['Address']['State'] ?? ($c['State'] ?? ''),
            'postal_code' => $c['Address']['ZipCode'] ?? ($c['ZipCode'] ?? ''),
            'country' => $c['Address']['Country'] ?? ($c['Country'] ?? ''),
            'phone' => $c['Phone']['Phone']['Number'] ?? ($c['Phone'] ?? ''),
            'fax' => $c['Phone']['Fax']['Number'] ?? ($c['Fax'] ?? ''),
        ];
    }

    /**
     * Extract nameservers from the library's parsed domain-info `data`.
     * The library emits a plain `NameServers` string list.
     */
    protected function extractNameservers(array $data): array
    {
        $nsList = $data['NameServers'] ?? $data['NameServerList'] ?? [];

        if (is_string($nsList)) {
            return $nsList !== '' ? [$nsList] : [];
        }

        if (! is_array($nsList)) {
            return [];
        }

        $nameservers = [];
        array_walk_recursive($nsList, function ($ns) use (&$nameservers) {
            if (is_string($ns) && trim($ns) !== '') {
                $nameservers[] = trim($ns);
            }
        });

        return array_values($nameservers);
    }

    /**
     * Parse a library TLD row (getTldList shape) into procp's pricing row.
     * Library row: id/status/maxchar/maxperiod/minchar/minperiod/tld/pricing/currencies
     * where pricing is keyed [tradeType][period] => price string.
     */
    protected function parseTldPricing(array $tld): ?array
    {
        $tldName = (string) ($tld['tld'] ?? '');
        if ($tldName === '') {
            return null;
        }

        $pricing = $tld['pricing'] ?? [];
        $currencies = $tld['currencies'] ?? [];

        return [
            'tld' => $tldName,
            'register' => $pricing['registration'][1] ?? null,
            'transfer' => $pricing['transfer'][1] ?? null,
            'renew' => $pricing['renew'][1] ?? null,
            'currency' => $currencies['registration'] ?? ($currencies['renew'] ?? null),
            'min_period' => (int) ($tld['minperiod'] ?? 1),
            'max_period' => (int) ($tld['maxperiod'] ?? 10),
        ];
    }

    /**
     * Normalize DNA raw domain status to procp's enum where possible.
     */
    protected function normalizeDomainStatus(string $status): string
    {
        $s = strtolower(trim($status));

        return match (true) {
            $s === '' => '',
            in_array($s, ['active', 'ok', 'registered'], true) => 'active',
            str_contains($s, 'pending') => 'pending',
            str_contains($s, 'suspend') || str_contains($s, 'hold') => 'suspended',
            default => $s,
        };
    }

    /**
     * Parse various boolean representations from the API.
     */
    protected function parseBool($value): bool
    {
        if (is_bool($value)) {
            return $value;
        }

        if (is_string($value)) {
            return in_array(strtolower($value), ['true', '1', 'yes', 'on', 'enabled', 'active'], true);
        }

        return (bool) $value;
    }

    /**
     * Whether test mode is enabled (SOAP OTE — REST ignores it).
     */
    protected function isTestMode(): bool
    {
        return filter_var($this->config['test_mode'] ?? false, FILTER_VALIDATE_BOOLEAN);
    }

    // -------------------------------------------------------------------------
    //  TLD-specific extra fields (.tr TRABIS: TCKN/VKN/tax_office) — PRC-870
    // -------------------------------------------------------------------------

    /**
     * Declare .tr identity extra-fields collected by the generic extra-fields
     * pipeline (store + admin). Values arrive back as $params['additional_fields']
     * in registerDomain(). Reuses the shared dummyregistrar_tr_* translation keys.
     */
    public function getExtraFields(string $tld): array
    {
        if (! in_array($tld, ['tr', 'com.tr', 'net.tr', 'org.tr'], true)) {
            return [];
        }

        return [
            'identity_type' => [
                'type' => 'select',
                'label' => __('admin/common.dummyregistrar_tr_identity_type'),
                'options' => [
                    'individual' => __('admin/common.dummyregistrar_tr_individual'),
                    'corporate' => __('admin/common.dummyregistrar_tr_corporate'),
                ],
                'required' => true,
                'order' => 10,
            ],
            'tckn' => [
                'type' => 'text',
                'label' => __('admin/common.dummyregistrar_tr_tckn'),
                'required' => true,
                'depends_on' => ['identity_type' => 'individual'],
                'validation' => [
                    'length' => 11,
                    'pattern' => '/^\d{11}$/',
                ],
                'order' => 20,
            ],
            'vkn' => [
                'type' => 'text',
                'label' => __('admin/common.dummyregistrar_tr_vkn'),
                'required' => true,
                'depends_on' => ['identity_type' => 'corporate'],
                'validation' => [
                    'length' => 10,
                    'pattern' => '/^\d{10}$/',
                ],
                'order' => 30,
            ],
            'tax_office' => [
                'type' => 'text',
                'label' => __('admin/common.dummyregistrar_tr_tax_office'),
                'required' => true,
                'depends_on' => ['identity_type' => 'corporate'],
                'order' => 40,
            ],
        ];
    }

    /**
     * Algorithmic per-key validation (TCKN MERNIS mod-10 checksum). Declarative
     * length/pattern rules are already applied by core before this runs.
     * Return contract: true = valid; string = ALREADY-TRANSLATED error message.
     */
    public function validateExtraField(string $tld, string $key, mixed $value, array $allValues): bool|string
    {
        if ($key === 'tckn' && is_string($value) && $value !== '') {
            if (! $this->isValidTcknChecksum($value)) {
                return __('admin/common.dummyregistrar_invalid_tckn');
            }
        }

        return true;
    }

    /**
     * TCKN MERNIS mod-10 checksum. 11 digits, first != 0.
     */
    private function isValidTcknChecksum(string $tckn): bool
    {
        if (! preg_match('/^\d{11}$/', $tckn)) {
            return false;
        }
        if ($tckn[0] === '0') {
            return false;
        }

        $d = array_map('intval', str_split($tckn));

        $d10 = ((($d[0] + $d[2] + $d[4] + $d[6] + $d[8]) * 7)
               - ($d[1] + $d[3] + $d[5] + $d[7])) % 10;
        if ($d10 < 0) {
            $d10 += 10;
        }
        if ($d10 !== $d[9]) {
            return false;
        }

        $d11 = array_sum(array_slice($d, 0, 10)) % 10;

        return $d11 === $d[10];
    }
}
