<?php

namespace App\Http\Controllers\Admin\Api;

use App\Http\Controllers\Controller;
use App\Jobs\ProcessDomainRegisterJob;
use App\Jobs\ProcessDomainRenewJob;
use App\Jobs\ProcessDomainTransferJob;
use App\Models\ClientDomain;
use App\Services\DomainProvisioningService;
use App\Services\ProvisioningStatusService;
use App\Traits\LogsAdminActivity;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class DomainProvisioningController extends Controller
{
    use LogsAdminActivity;
    protected ProvisioningStatusService $provisioningStatus;
    protected DomainProvisioningService $provisioningService;

    public function __construct(
        ProvisioningStatusService $provisioningStatus,
        DomainProvisioningService $provisioningService
    ) {
        $this->provisioningStatus = $provisioningStatus;
        $this->provisioningService = $provisioningService;
    }

    /**
     * Resolve module context for a sync operation.
     *
     * Returns a 4xx JsonResponse when module is missing / unknown. On success,
     * returns the context array from DomainProvisioningService::buildModuleContext.
     *
     * Every sync endpoint in this controller uses this — guarantees:
     *   - `$domain->module` is source of truth (not TLD default)
     *   - `additional_fields` and contact_details are always available
     *   - TLD string is extracted from domain name (not from drifted tld_id)
     *
     * @return array|JsonResponse
     */
    protected function resolveModuleContext(ClientDomain $domain)
    {
        if (! $domain->module) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_module_not_defined_domain'),
            ], 422);
        }

        try {
            return $this->provisioningService->buildModuleContext($domain);
        } catch (\Throwable $e) {
            \Log::error('Domain module context resolution failed', [
                'domain_id' => $domain->id,
                'module' => $domain->module,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_registrar_module_not_found', ['name' => $domain->module]),
            ], 422);
        }
    }

    /**
     * Domain registration işlemini başlatır
     */
    public function register(ClientDomain $domain): JsonResponse
    {
        // Check if domain has module
        if (!$domain->module) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_module_not_defined_domain'),
            ], 422);
        }

        // Check if there's already a running provisioning
        $runningJobId = $this->provisioningStatus->findRunningJob(
            ProvisioningStatusService::ENTITY_DOMAIN,
            $domain->id,
            ProvisioningStatusService::ACTION_REGISTER
        );

        if ($runningJobId) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_operation_already_in_progress_domain'),
                'provisioning_id' => $runningJobId,
            ], 409);
        }

        // Create provisioning status in Redis
        $provisioningId = $this->provisioningStatus->create(
            actionType: ProvisioningStatusService::ACTION_REGISTER,
            entityType: ProvisioningStatusService::ENTITY_DOMAIN,
            entityId: $domain->id,
            moduleSlug: strtolower($domain->module),
            message: __('admin/provisioning.messages_operation_queued')
        );

        // Dispatch register job
        ProcessDomainRegisterJob::dispatch($domain->id, $provisioningId);

        return response()->json([
            'success' => true,
            'message' => __('admin/provisioning.messages_operation_started'),
            'provisioning_id' => $provisioningId,
        ]);
    }

    /**
     * Domain transfer işlemini başlatır
     */
    public function transfer(ClientDomain $domain): JsonResponse
    {
        if (!$domain->module) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_module_not_defined_domain'),
            ], 422);
        }

        $runningJobId = $this->provisioningStatus->findRunningJob(
            ProvisioningStatusService::ENTITY_DOMAIN,
            $domain->id,
            ProvisioningStatusService::ACTION_TRANSFER
        );

        if ($runningJobId) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_operation_already_in_progress_domain'),
                'provisioning_id' => $runningJobId,
            ], 409);
        }

        $provisioningId = $this->provisioningStatus->create(
            actionType: ProvisioningStatusService::ACTION_TRANSFER,
            entityType: ProvisioningStatusService::ENTITY_DOMAIN,
            entityId: $domain->id,
            moduleSlug: strtolower($domain->module),
            message: __('admin/provisioning.messages_operation_queued')
        );

        ProcessDomainTransferJob::dispatch($domain->id, $provisioningId);

        return response()->json([
            'success' => true,
            'message' => __('admin/provisioning.messages_operation_started'),
            'provisioning_id' => $provisioningId,
        ]);
    }

    /**
     * Domain renew işlemini başlatır
     */
    public function renew(ClientDomain $domain): JsonResponse
    {
        if (!$domain->module) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_module_not_defined_domain'),
            ], 422);
        }

        $runningJobId = $this->provisioningStatus->findRunningJob(
            ProvisioningStatusService::ENTITY_DOMAIN,
            $domain->id,
            ProvisioningStatusService::ACTION_RENEW
        );

        if ($runningJobId) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_operation_already_in_progress_domain'),
                'provisioning_id' => $runningJobId,
            ], 409);
        }

        $provisioningId = $this->provisioningStatus->create(
            actionType: ProvisioningStatusService::ACTION_RENEW,
            entityType: ProvisioningStatusService::ENTITY_DOMAIN,
            entityId: $domain->id,
            moduleSlug: strtolower($domain->module),
            message: __('admin/provisioning.messages_operation_queued')
        );

        ProcessDomainRenewJob::dispatch($domain->id, $provisioningId);

        return response()->json([
            'success' => true,
            'message' => __('admin/provisioning.messages_operation_started'),
            'provisioning_id' => $provisioningId,
        ]);
    }

    /**
     * Get domain contact details (senkron)
     */
    public function getContactDetails(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            $params = $context['base_params'];

            $result = app(\App\Core\Registrar\RegistrarManager::class)->call($context['module'], 'getContactDetails', $params);

            // Normalize: some modules return 'groups' at top level, some inside 'data'
            $contactGroups = $result['groups'] ?? $result['data']['groups'] ?? $result['data'] ?? null;

            return response()->json([
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_operation_completed'),
                'data' => $contactGroups,
            ]);

        } catch (\Exception $e) {
            \Log::error('Domain get contact details failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * Domain contact details modify işlemini yapar (senkron)
     */
    public function modifyContact(Request $request, ClientDomain $domain): JsonResponse
    {
        // Validate groups parameter
        $validated = $request->validate([
            'groups' => 'required|array',
            'groups.*' => 'array',
        ]);

        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            // Admin-submitted groups pass through UNMODIFIED to the module.
            //
            // Why no DB fallback merge: the JS flow (domain-detail.js
            // loadContactDetails → buildContactForm → saveContactDetails)
            // ALWAYS round-trips EVERY field the module originally returned
            // via getContactDetails. The keys admin sends back are exactly
            // the module's own keys — whatever they are (`postal_code`,
            // `zipcode`, `statecode`, `firstname`, `first_name` …) — and
            // include every group the module exposes (`Registrant`, `Owner`,
            // `Admin`, `Tech`, `Billing`, or arbitrary module-specific
            // names). Merging with a hardcoded canonical client-profile
            // shape injects keys the module doesn't recognize AND silently
            // drops groups whose names don't match our whitelist.
            //
            // Unknown-field sanitization is the module's responsibility —
            // it knows its own contract.
            $params = array_merge($context['base_params'], [
                'groups' => $validated['groups'],
            ]);

            $result = app(\App\Core\Registrar\RegistrarManager::class)->call($context['module'], 'setContactDetails', $params);

            return response()->json([
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_operation_completed'),
                'data' => $result['data'] ?? null,
            ]);

        } catch (\Exception $e) {
            \Log::error('Domain modify contact failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * Get EPP code işlemini yapar (senkron)
     */
    public function getEppCode(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            $result = app(\App\Core\Registrar\RegistrarManager::class)->call($context['module'], 'getEppCode', $context['base_params']);

            return response()->json([
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_operation_completed'),
                'data' => $result['data'] ?? null,
            ]);

        } catch (\Exception $e) {
            \Log::error('Domain get EPP code failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * ID Protection enable işlemini yapar (senkron)
     */
    public function idProtectionEnable(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            $params = array_merge($context['base_params'], [
                'privacy_enabled' => true,
            ]);

            $result = $context['module']->updatePrivacySettings($params);

            // Update domain if success
            if (isset($result['status']) && $result['status'] === 'success') {
                $domain->update(['id_protection' => true]);
            }

            return response()->json([
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_operation_completed'),
                'data' => $result['data'] ?? null,
            ]);

        } catch (\Exception $e) {
            \Log::error('Domain ID protection enable failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * ID Protection disable işlemini yapar (senkron)
     */
    public function idProtectionDisable(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            $params = array_merge($context['base_params'], [
                'privacy_enabled' => false,
            ]);

            $result = $context['module']->updatePrivacySettings($params);

            // Update domain if success
            if (isset($result['status']) && $result['status'] === 'success') {
                $domain->update(['id_protection' => false]);
            }

            return response()->json([
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_operation_completed'),
                'data' => $result['data'] ?? null,
            ]);

        } catch (\Exception $e) {
            \Log::error('Domain ID protection disable failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * Domain Lock enable işlemini yapar (senkron)
     */
    public function domainLockEnable(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            $params = array_merge($context['base_params'], [
                'lock_status' => true,
            ]);

            $result = $context['module']->updateDomainLock($params);

            return response()->json([
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_domain_lock_enabled'),
                'data' => $result['data'] ?? null,
            ]);

        } catch (\Exception $e) {
            \Log::error('Domain lock enable failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * PRC-783 — Tek domain'i registrar'dan manuel sync et (expiry + status persist).
     * Motor DomainSyncService::syncDomain (module-is-truth uyumlu); burada yalnız tetik + log.
     */
    public function syncDomain(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context; // modül yoksa 422
        }

        try {
            $result = app(\App\Services\DomainSyncService::class)->syncDomain($domain->id, 'admin');
            $success = ($result['status'] ?? null) === 'success';

            $this->logCustomAction(
                'domain_sync_manual',
                $domain,
                ['sync_source' => 'admin', 'result' => $success ? 'success' : 'error'],
                "Manuel domain sync: {$domain->domain}",
                $domain->client_id,
            );

            return response()->json([
                'success' => $success,
                'message' => $success
                    ? __('admin/provisioning.messages_domain_synced')
                    : ($result['message'] ?? __('admin/common.general_error')),
                'data' => $result['data'] ?? null,
            ]);
        } catch (\Exception $e) {
            \Log::error('Manual domain sync failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * Domain Lock disable işlemini yapar (senkron)
     */
    public function domainLockDisable(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            $params = array_merge($context['base_params'], [
                'lock_status' => false,
            ]);

            $result = $context['module']->updateDomainLock($params);

            return response()->json([
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_domain_lock_disabled'),
                'data' => $result['data'] ?? null,
            ]);

        } catch (\Exception $e) {
            \Log::error('Domain lock disable failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * Domain silme işlemini yapar (admin-only, senkron)
     */
    public function delete(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            // Check if registrar supports domain deletion
            $features = $context['module']->getSupportedFeatures();
            if (!in_array('delete', $features)) {
                return response()->json([
                    'success' => false,
                    'message' => __('admin/provisioning.messages_domain_delete_not_supported'),
                ], 422);
            }

            $result = app(\App\Core\Registrar\RegistrarManager::class)->call($context['module'], 'deleteDomain', $context['base_params']);

            if (isset($result['status']) && $result['status'] === 'success') {
                $domain->update([
                    'status' => 'cancelled',
                    'lifecycle_status' => ClientDomain::LIFECYCLE_DELETED,
                ]);

                $this->logActivity(
                    action: self::ACTION_DELETED,
                    entityType: 'domain',
                    entityId: $domain->id,
                    description: "Domain {$domain->domain} deleted at registrar",
                    properties: ['domain' => $domain->domain, 'module' => $context['module_name']],
                    clientId: $domain->client_id
                );
            }

            return response()->json([
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_operation_completed'),
                'data' => $result['data'] ?? null,
            ]);

        } catch (\Exception $e) {
            \Log::error('Domain delete failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
            ], 500);
        }
    }

    /**
     * İşlem durumunu sorgular (AJAX polling için)
     */
    public function status(string $provisioningId): JsonResponse
    {
        $status = $this->provisioningStatus->get($provisioningId);

        if (! $status) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_operation_not_found'),
            ], 404);
        }

        return response()->json([
            'success' => true,
            'data' => $status,
        ]);
    }

    /**
     * Domain info, nameservers, privacy status bilgilerini getirir
     */
    public function getDomainInfo(ClientDomain $domain): JsonResponse
    {
        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            // Decorate the empty-state data for the dashboard UI that consumes this.
            $original = $context->getData(true);
            $original['data'] = [
                'nameservers' => [],
                'privacy_enabled' => false,
                'id_protection' => $domain->id_protection ?? false,
            ];
            return response()->json($original, $context->status());
        }

        try {
            $moduleInstance = $context['module'];

            $result = $moduleInstance->getDomainDetails($context['base_params']);

            if (isset($result['status']) && $result['status'] === 'success' && isset($result['data'])) {
                $data = $result['data'];

                // Get admin buttons if available
                $adminButtons = [];
                if (method_exists($moduleInstance, 'adminButtons')) {
                    $adminButtonsData = $moduleInstance->adminButtons($context['base_params']);
                    if (is_array($adminButtonsData)) {
                        $adminButtons = $adminButtonsData;
                    }
                }

                // Get admin output if available
                $adminOutput = '';
                if (method_exists($moduleInstance, 'adminOutput')) {
                    $adminOutput = $moduleInstance->adminOutput(array_merge($context['base_params'], [
                        'registered_at' => $domain->create_date?->format('d.m.Y') ?? 'N/A',
                        'expires_at' => $domain->expire_date?->format('d.m.Y') ?? 'N/A',
                    ]));
                }

                return response()->json([
                    'success' => true,
                    'message' => __('admin/provisioning.messages_domain_info_retrieved'),
                    'data' => [
                        'nameservers' => $data['nameservers'] ?? [],
                        'privacy_enabled' => $data['privacy_enabled'] ?? false,
                        'id_protection' => $domain->id_protection ?? false,
                        'locked' => $data['locked'] ?? false,
                        'auto_renew' => $data['auto_renew'] ?? false,
                        'admin_buttons' => $adminButtons,
                        'admin_output' => $adminOutput,
                    ],
                ]);
            }

            throw new \Exception($result['message'] ?? __('admin/provisioning.messages_domain_info_failed'));

        } catch (\Exception $e) {
            \Log::error('Get domain info failed', [
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => __('admin/common.general_error'),
                'data' => [
                    'nameservers' => [],
                    'privacy_enabled' => false,
                    'id_protection' => $domain->id_protection ?? false,
                ],
            ], 500);
        }
    }

    /**
     * Execute a custom module admin action for a domain
     */
    public function customAction(ClientDomain $domain, Request $request): JsonResponse
    {
        $action = $request->input('action');

        if (!$action) {
            return response()->json([
                'success' => false,
                'message' => __('admin/provisioning.messages_action_required'),
            ], 422);
        }

        $context = $this->resolveModuleContext($domain);
        if ($context instanceof JsonResponse) {
            return $context;
        }

        try {
            $moduleInstance = $context['module'];

            if (!method_exists($moduleInstance, 'adminCustomAction')) {
                throw new \Exception(__('admin/provisioning.messages_custom_action_not_supported'));
            }

            // Request payload overrides the base params for this ad-hoc action
            // (module contract dictates what it needs), but keeps sld/tld/
            // additional_fields available when not explicitly overridden.
            $params = array_merge($context['base_params'], $request->except(['action']));

            $result = $moduleInstance->adminCustomAction($action, $params);

            $responseData = [
                'success' => isset($result['status']) && $result['status'] === 'success',
                'message' => $result['message'] ?? __('admin/provisioning.messages_operation_completed'),
            ];

            if (isset($result['data'])) {
                $responseData['data'] = $result['data'];
            }

            return response()->json($responseData);

        } catch (\Exception $e) {
            \Log::error('Domain custom action failed', [
                'domain_id' => $domain->id,
                'action' => $action,
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 500);
        }
    }
}
