<?php

namespace App\Services;

use App\Core\Billing\BillingManager;
use App\Core\Registrar\RegistrarManager;
use App\Jobs\SendEmailJob;
use App\Models\ClientDomain;
use App\Models\Invoice;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Setting;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class DomainLifecycleService
{
    protected BillingManager $billingManager;

    protected TaxCalculationService $taxService;

    protected RegistrarManager $registrarManager;

    protected InvoiceService $invoiceService;

    public function __construct(
        BillingManager $billingManager,
        TaxCalculationService $taxService,
        RegistrarManager $registrarManager,
        InvoiceService $invoiceService
    ) {
        $this->billingManager = $billingManager;
        $this->taxService = $taxService;
        $this->registrarManager = $registrarManager;
        $this->invoiceService = $invoiceService;
    }

    /**
     * Process all expired domains through their lifecycle stages.
     * Called daily by the scheduled command.
     */
    public function processExpiredDomains(): array
    {
        $stats = [
            'expired' => 0,
            'grace' => 0,
            'redemption' => 0,
            'errors' => [],
        ];

        $today = Carbon::today();

        // 1. Active domains past expiry → expired
        $this->processActiveToExpired($today, $stats);

        // 2. Expired domains past grace_period → grace
        $this->processExpiredToGrace($today, $stats);

        // 3. Grace domains past redemption threshold → redemption
        $this->processGraceToRedemption($today, $stats);

        // PRC-325 / F-15 — Auto-delete after redemption removed.
        // Registrar takes over once a domain enters redemption; the
        // panel no longer marks rows as LIFECYCLE_DELETED on a schedule.
        // Admin can still manually delete via deleteDomain().

        return $stats;
    }

    /**
     * Transition active domains with past expiry dates to expired status.
     */
    protected function processActiveToExpired(Carbon $today, array &$stats): void
    {
        $domains = ClientDomain::where('lifecycle_status', ClientDomain::LIFECYCLE_ACTIVE)
            ->whereNotNull('expire_date')
            ->where('expire_date', '<', $today)
            ->with(['tld', 'client'])
            ->get();

        foreach ($domains as $domain) {
            try {
                $this->transitionToExpired($domain);
                $stats['expired']++;
            } catch (\Exception $e) {
                $stats['errors'][] = "Expired transition failed for {$domain->domain}: {$e->getMessage()}";
                Log::error('Domain lifecycle: failed to transition to expired', [
                    'domain_id' => $domain->id,
                    'domain' => $domain->domain,
                    'error' => $e->getMessage(),
                ]);
            }
        }
    }

    /**
     * Transition expired domains past grace period to grace status.
     */
    protected function processExpiredToGrace(Carbon $today, array &$stats): void
    {
        $domains = ClientDomain::where('lifecycle_status', ClientDomain::LIFECYCLE_EXPIRED)
            ->whereNotNull('grace_start_date')
            ->with(['tld', 'client'])
            ->get();

        foreach ($domains as $domain) {
            $tld = $domain->tld;
            if (! $tld || ! $tld->grace_period) {
                continue;
            }

            $graceEndDate = Carbon::parse($domain->grace_start_date)->addDays($tld->grace_period);

            if ($today->gte($graceEndDate)) {
                try {
                    $this->transitionToGrace($domain);
                    $stats['grace']++;
                } catch (\Exception $e) {
                    $stats['errors'][] = "Grace transition failed for {$domain->domain}: {$e->getMessage()}";
                    Log::error('Domain lifecycle: failed to transition to grace', [
                        'domain_id' => $domain->id,
                        'domain' => $domain->domain,
                        'error' => $e->getMessage(),
                    ]);
                }
            }
        }
    }

    /**
     * Transition grace domains past redemption threshold to redemption status.
     */
    protected function processGraceToRedemption(Carbon $today, array &$stats): void
    {
        $domains = ClientDomain::where('lifecycle_status', ClientDomain::LIFECYCLE_GRACE)
            ->whereNotNull('grace_start_date')
            ->with(['tld', 'client'])
            ->get();

        foreach ($domains as $domain) {
            $tld = $domain->tld;
            if (! $tld || ! $tld->redemption_period) {
                continue;
            }

            // Grace + redemption threshold: grace_start_date + grace_period + redemption_period
            $gracePeriod = $tld->grace_period ?? 0;
            $redemptionThreshold = Carbon::parse($domain->grace_start_date)
                ->addDays($gracePeriod)
                ->addDays($tld->redemption_period);

            if ($today->gte($redemptionThreshold)) {
                try {
                    $this->transitionToRedemption($domain);
                    $stats['redemption']++;
                } catch (\Exception $e) {
                    $stats['errors'][] = "Redemption transition failed for {$domain->domain}: {$e->getMessage()}";
                    Log::error('Domain lifecycle: failed to transition to redemption', [
                        'domain_id' => $domain->id,
                        'domain' => $domain->domain,
                        'error' => $e->getMessage(),
                    ]);
                }
            }
        }
    }

    /**
     * Transition a domain to expired status.
     * When `enable_renewal_orders` is on, creates a renewal Order + Invoice
     * and emails the client. When off, the lifecycle still marks the domain
     * expired but no renewal invoice/notification is produced (admin opted
     * out of the renewal flow entirely — PRC-323 / B-6).
     */
    public function transitionToExpired(ClientDomain $domain): void
    {
        DB::beginTransaction();

        try {
            $domain->update([
                'lifecycle_status' => ClientDomain::LIFECYCLE_EXPIRED,
                'grace_start_date' => $domain->expire_date,
            ]);

            // PRC-323 / B-6 — Master gate. Skip invoice + email when off.
            $renewalsEnabled = \App\Services\Domain\DomainPolicyGuard::renewalEnabled();
            $invoice = null;
            if ($renewalsEnabled) {
                $invoice = $this->createRenewalOrderAndInvoice($domain);
            }

            DB::commit();

            // PRC-628 final review C2 — Domain Expiry Warning ve Domain Expired
            // ARTIK MUTUALLY EXCLUSIVE: ACTIVE→EXPIRED geçişinde TEK mail gider.
            // Önceden ikisi de aynı geçişte (Warning koşulsuz + Expired invoice
            // varsa) gidip çift mail üretiyordu.
            //   • Renewal AÇIK  (invoice var)  → 'Domain Expired'
            //     ("süreniz doldu, yenileme faturanız üretildi").
            //   • Renewal KAPALI (invoice null) → 'Domain Expiry Warning'
            //     ("süreniz doldu, otomatik yenileme yok").
            // İdempotency: processActiveToExpired YALNIZ LIFECYCLE_ACTIVE
            // domainleri seçer, status bu transaction içinde EXPIRED'a çevrilir
            // → aynı domain bu yola ikinci kez giremez.
            if ($invoice !== null) {
                $this->sendLifecycleEmail('Domain Expired', $domain, [
                    'DOMAIN' => $domain->domain,
                    'EXPIRY_DATE' => $domain->expire_date?->format('d/m/Y') ?? '',
                    'RENEWAL_URL' => route('store.payment.show', $invoice->id),
                    'AMOUNT' => number_format($invoice->total_amount, 2),
                ]);

                Log::info('Domain lifecycle: transitioned to expired', [
                    'domain_id' => $domain->id,
                    'domain' => $domain->domain,
                    'invoice_id' => $invoice->id,
                ]);
            } else {
                $this->sendLifecycleEmail('Domain Expiry Warning', $domain, [
                    'DOMAIN' => $domain->domain,
                    'CLIENT_NAME' => $domain->client
                        ? trim($domain->client->first_name . ' ' . $domain->client->last_name)
                        : '',
                    'EXPIRY_DATE' => $domain->expire_date?->format('d/m/Y') ?? '',
                ]);

                Log::info('Domain lifecycle: transitioned to expired (renewals disabled, no invoice)', [
                    'domain_id' => $domain->id,
                    'domain' => $domain->domain,
                ]);
            }
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    /**
     * Transition a domain to grace period status.
     * Adds grace period fee to the existing renewal invoice.
     */
    public function transitionToGrace(ClientDomain $domain): void
    {
        DB::beginTransaction();

        try {
            $domain->update([
                'lifecycle_status' => ClientDomain::LIFECYCLE_GRACE,
            ]);

            $tld = $domain->tld;
            $graceFee = $tld->grace_period_fee ?? 0;

            // Find existing unpaid renewal invoice and add grace fee
            $invoice = $this->findLifecycleInvoice($domain, 'renewal');

            if ($invoice && $graceFee > 0) {
                $this->addGraceFee($invoice, $tld);
            }

            DB::commit();

            // Calculate grace end date for the email
            $gracePeriod = $tld->grace_period ?? 0;
            $redemptionPeriod = $tld->redemption_period ?? 0;
            $graceEndDate = Carbon::parse($domain->grace_start_date)
                ->addDays($gracePeriod)
                ->addDays($redemptionPeriod);

            $this->sendLifecycleEmail('Domain Grace Period Notice', $domain, [
                'DOMAIN' => $domain->domain,
                'GRACE_FEE' => number_format($graceFee, 2),
                'GRACE_END_DATE' => $graceEndDate->format('d/m/Y'),
                'PAYMENT_URL' => $invoice ? route('store.payment.show', $invoice->id) : '',
            ]);

            Log::info('Domain lifecycle: transitioned to grace', [
                'domain_id' => $domain->id,
                'domain' => $domain->domain,
                'grace_fee' => $graceFee,
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    /**
     * Transition a domain to redemption status.
     * Cancels grace invoice and creates a new redemption invoice.
     */
    public function transitionToRedemption(ClientDomain $domain): void
    {
        DB::beginTransaction();

        try {
            $domain->update([
                'lifecycle_status' => ClientDomain::LIFECYCLE_REDEMPTION,
                'redemption_start_date' => Carbon::today(),
            ]);

            // Cancel existing grace/renewal invoice
            $existingInvoice = $this->findLifecycleInvoice($domain, 'renewal');
            if ($existingInvoice) {
                $this->cancelInvoice($existingInvoice);
            }

            $graceInvoice = $this->findLifecycleInvoice($domain, 'grace');
            if ($graceInvoice) {
                $this->cancelInvoice($graceInvoice);
            }

            // Create redemption invoice
            $tld = $domain->tld;
            $invoice = $this->createRedemptionInvoice($domain, $tld);

            DB::commit();

            // Calculate redemption end date
            $redemptionEndDate = Carbon::today()->addDays($tld->redemption_period ?? 30);

            $this->sendLifecycleEmail('Domain Redemption Period Notice', $domain, [
                'DOMAIN' => $domain->domain,
                'REDEMPTION_FEE' => number_format($tld->redemption_period_fee ?? 0, 2),
                'REDEMPTION_END_DATE' => $redemptionEndDate->format('d/m/Y'),
                'PAYMENT_URL' => route('store.payment.show', $invoice->id),
            ]);

            Log::info('Domain lifecycle: transitioned to redemption', [
                'domain_id' => $domain->id,
                'domain' => $domain->domain,
                'invoice_id' => $invoice->id,
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    /**
     * Delete a domain after redemption period expires.
     * Cancels all unpaid invoices and soft deletes the domain.
     */
    public function deleteDomain(ClientDomain $domain): void
    {
        DB::beginTransaction();

        try {
            $domain->update([
                'lifecycle_status' => ClientDomain::LIFECYCLE_DELETED,
                'status' => 'cancelled',
            ]);

            // Cancel all related unpaid invoices
            $unpaidInvoices = Invoice::where('domain_id', $domain->id)
                ->whereIn('status', [Invoice::STATUS_UNPAID, Invoice::STATUS_PENDING])
                ->get();

            foreach ($unpaidInvoices as $invoice) {
                $this->cancelInvoice($invoice);
            }

            // Soft delete the domain
            $domain->delete();

            DB::commit();

            $this->sendLifecycleEmail('Domain Deleted', $domain, [
                'DOMAIN' => $domain->domain,
                'CLIENT_NAME' => $domain->client
                    ? $domain->client->first_name . ' ' . $domain->client->last_name
                    : '',
            ]);

            Log::info('Domain lifecycle: domain deleted', [
                'domain_id' => $domain->id,
                'domain' => $domain->domain,
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    /**
     * Handle renewal payment for a lifecycle invoice.
     * Resets the domain to active and updates the expiry date.
     */
    public function handleRenewalPayment(Invoice $invoice): void
    {
        if (! $invoice->domain_id || ! $invoice->domain_lifecycle_type) {
            return;
        }

        $domain = ClientDomain::withTrashed()->with(['tld', 'client'])->find($invoice->domain_id);

        if (! $domain) {
            Log::warning('Domain lifecycle: domain not found for renewal payment', [
                'invoice_id' => $invoice->id,
                'domain_id' => $invoice->domain_id,
            ]);

            return;
        }

        DB::beginTransaction();

        try {
            // Try to renew via registrar
            $this->renewViaRegistrar($domain);

            // Calculate new expiry date
            $period = $domain->period ?: 1;
            $currentExpiry = $domain->expire_date ?? Carbon::today();
            $newExpiryDate = Carbon::parse($currentExpiry)->addYears($period);

            // If domain was soft-deleted (from deletion stage), restore it
            if ($domain->trashed()) {
                $domain->restore();
            }

            $domain->update([
                'lifecycle_status' => ClientDomain::LIFECYCLE_ACTIVE,
                'status' => 'active',
                'expire_date' => $newExpiryDate,
                'next_due_date' => $newExpiryDate,
                'grace_start_date' => null,
                'redemption_start_date' => null,
            ]);

            // Cancel any remaining unpaid lifecycle invoices for this domain
            $otherUnpaidInvoices = Invoice::where('domain_id', $domain->id)
                ->where('id', '!=', $invoice->id)
                ->whereIn('status', [Invoice::STATUS_UNPAID, Invoice::STATUS_PENDING])
                ->whereNotNull('domain_lifecycle_type')
                ->get();

            foreach ($otherUnpaidInvoices as $otherInvoice) {
                $this->cancelInvoice($otherInvoice);
            }

            DB::commit();

            $this->sendLifecycleEmail('Domain Renewed', $domain, [
                'DOMAIN' => $domain->domain,
                'NEW_EXPIRY_DATE' => $newExpiryDate->format('d/m/Y'),
            ]);

            Log::info('Domain lifecycle: domain renewed via payment', [
                'domain_id' => $domain->id,
                'domain' => $domain->domain,
                'invoice_id' => $invoice->id,
                'new_expiry_date' => $newExpiryDate->toDateString(),
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Domain lifecycle: renewal payment handling failed', [
                'domain_id' => $domain->id,
                'invoice_id' => $invoice->id,
                'error' => $e->getMessage(),
            ]);
            throw $e;
        }
    }

    /**
     * Create an Order for domain renewal, then create an Invoice from that Order.
     * Used when the 'enable_renewal_orders' setting is active.
     */
    protected function createRenewalOrderAndInvoice(ClientDomain $domain): Invoice
    {
        return $this->buildRenewalOrderInvoice(
            $domain,
            $domain->period ?: 1,
            $this->billingManager->getDomainRenewalPricePublic($domain)
        );
    }

    /**
     * PRC-645 — Yenileme Order+Invoice'unu kuran DRY builder.
     * period ve birim fiyat parametreden gelir; OrderItem'da domain_action='renew' GARANTİ
     * (ödeme→extend zinciri HandleInvoiceStatusChange bunu arar).
     */
    private function buildRenewalOrderInvoice(ClientDomain $domain, int $period, float $unitPrice): Invoice
    {
        $client = $domain->client;

        $taxResult = $this->taxService->calculateTaxAmount(
            amount: $unitPrice,
            client: $client,
            productType: 'domain',
            product: null,
            countryCode: null
        );

        // Create the Order
        $order = Order::create([
            'uuid' => (string) \Illuminate\Support\Str::uuid(),
            'order_number' => Order::generateOrderNumber(),
            'client_id' => $domain->client_id,
            'currency_id' => $client->currency_id ?? null,
            'amount' => $taxResult['total'],
            'subtotal' => $unitPrice,
            'tax_amount' => $taxResult['tax_amount'],
            'discount_amount' => 0,
            'setup_amount' => 0,
            'total' => $taxResult['total'],
            'status' => 'pending',
            'payment_status' => 'pending',
            'notes' => __('admin/billing.domain_renewal_invoice', ['domain' => $domain->domain]),
            'send_email' => false,
            'create_invoice' => true,
            'ordered_at' => now(),
        ]);

        // Create the OrderItem
        OrderItem::create([
            'order_id' => $order->id,
            'type' => 'domain',
            'name' => $domain->domain,
            'description' => __('admin/billing.due_domain_renewal', ['domain' => $domain->domain]),
            'quantity' => 1,
            'unit_price' => $unitPrice,
            'total_price' => $unitPrice,
            'domain_name' => $domain->domain,
            'domain_action' => 'renew',
            'period' => $period,
            'period_type' => 'year',
        ]);

        // Reload order with items for the invoice service
        $order->load('items');

        // Create invoice from the order
        $invoice = $this->invoiceService->createInvoiceFromOrder($order);

        // Tag the invoice with domain lifecycle info
        $invoice->update([
            'domain_lifecycle_type' => 'renewal',
            'domain_id' => $domain->id,
        ]);

        return $invoice;
    }

    /**
     * PRC-645 — Admin tarafından period seçimli yenileme siparişi.
     * Cap (max_years) doğrular, period-specific renewal fiyatını bulur, Order+Invoice kurar.
     * enable_renewal_orders'tan BAĞIMSIZ (admin kaçış kapısı).
     */
    public function createAdminRenewalOrder(ClientDomain $domain, int $period): Invoice
    {
        if (! in_array($domain->status, ['active', 'expired'], true)) {
            throw new \InvalidArgumentException(__('admin/clients.domain_renewal_status_invalid'));
        }

        $cap = \App\Services\Domain\DomainPolicyGuard::maxRenewalYears($domain);
        if ($cap < 1) {
            throw new \InvalidArgumentException(__('admin/clients.domain_renewal_max_reached'));
        }
        if ($period < 1 || $period > $cap) {
            throw new \InvalidArgumentException(__('admin/clients.domain_renewal_period_invalid', ['max' => $cap]));
        }

        $client = $domain->client;
        $pricing = \App\Models\Pricing::where('type', 'domain')
            ->where('relation_id', $domain->tld_id)
            ->where('segment', 'renew')
            ->where('period', $period)
            ->when($client->currency_id, fn ($q) => $q->where('currency_id', $client->currency_id))
            ->first();
        if (! $pricing) {
            throw new \InvalidArgumentException(__('admin/clients.domain_renewal_pricing_missing'));
        }

        return $this->buildRenewalOrderInvoice($domain, $period, (float) $pricing->amount);
    }

    /**
     * Add grace period fee to an existing invoice.
     */
    protected function addGraceFee(Invoice $invoice, $tld): void
    {
        $graceFee = (float) ($tld->grace_period_fee ?? 0);

        if ($graceFee <= 0) {
            return;
        }

        $invoice->update([
            'grace_period_fee' => $graceFee,
            'total_amount' => $invoice->total_amount + $graceFee,
            'domain_lifecycle_type' => 'grace',
        ]);
    }

    /**
     * Create a redemption invoice.
     */
    protected function createRedemptionInvoice(ClientDomain $domain, $tld): Invoice
    {
        $client = $domain->client;
        $redemptionFee = (float) ($tld->redemption_period_fee ?? 0);
        $renewalPrice = $this->billingManager->getDomainRenewalPricePublic($domain);
        $subtotal = $renewalPrice + $redemptionFee;

        $taxResult = $this->taxService->calculateTaxAmount(
            amount: $subtotal,
            client: $client,
            productType: 'domain',
            product: null,
            countryCode: null
        );

        $invoice = $this->billingManager->createInvoice([
            'client_id' => $domain->client_id,
            // PRC-369 — use the domain cycle's payment terms.
            'due_date' => Carbon::today()->addDays(
                \App\Services\Billing\PaymentTermsResolver::daysForCycle('domain')
            ),
            'subtotal' => $subtotal,
            'tax_rate' => $taxResult['rate'],
            'tax_amount' => $taxResult['tax_amount'],
            'total_amount' => $taxResult['total'],
            'product_type' => 'domain',
            'notes' => __('admin/billing.domain_redemption_invoice', ['domain' => $domain->domain]),
            'items' => [
                [
                    'description' => __('admin/billing.due_domain_renewal', ['domain' => $domain->domain]),
                    'quantity' => 1,
                    'unit_price' => $renewalPrice,
                    'line_total' => $renewalPrice,
                    'related_type' => 'domain',
                    'related_id' => $domain->tld_id,
                    'related_service_id' => $domain->id,
                ],
            ],
        ]);

        $invoice->update([
            'status' => Invoice::STATUS_UNPAID,
            'domain_lifecycle_type' => 'redemption',
            'domain_id' => $domain->id,
            'redemption_fee' => $redemptionFee,
        ]);

        return $invoice;
    }

    /**
     * Find an existing lifecycle invoice for a domain.
     */
    protected function findLifecycleInvoice(ClientDomain $domain, string $lifecycleType): ?Invoice
    {
        return Invoice::where('domain_id', $domain->id)
            ->where('domain_lifecycle_type', $lifecycleType)
            ->whereIn('status', [Invoice::STATUS_UNPAID, Invoice::STATUS_PENDING])
            ->first();
    }

    /**
     * Cancel an invoice.
     */
    protected function cancelInvoice(Invoice $invoice): void
    {
        $invoice->update([
            'status' => Invoice::STATUS_CANCELLED,
            'cancel_date' => now(),
        ]);
    }

    /**
     * PRC-775 — Domain başka registrar'a transfer çıkınca (status=transferred):
     * domainin AÇIK (unpaid/pending) faturalarını iptal eder (ödenmiş/historik
     * dokunulmaz) ve admin + müşteri bildirimini tek olaydan tetikler. Expire→
     * iptal mantığıyla tutarlı (aynı cancelInvoice yolu).
     *
     * Kredi-notu belge altyapısı yok; `transferred_invoice_action=credit_note`
     * açık faturayı yine iptal eder, fark admin bildirimindeki action_taken
     * sinyalidir (EU'da manuel kredi notu için).
     */
    public function handleTransferOut(ClientDomain $domain): void
    {
        $action = (string) Setting::getValue('transferred_invoice_action', 'cancel'); // cancel|credit_note

        $openInvoices = Invoice::where('domain_id', $domain->id)
            ->whereIn('status', [Invoice::STATUS_UNPAID, Invoice::STATUS_PENDING])
            ->get();

        foreach ($openInvoices as $invoice) {
            $this->cancelInvoice($invoice);
        }

        $actionTaken = $openInvoices->isEmpty() ? 'none' : $action;

        // Admin + müşteri bildirimi — tek olay (config/communication_events.php
        // 'DomainTransferred' map'i; audience gate ile çift-fire yok).
        app(\App\Core\Hook\HookBridge::class)->fireAction('DomainTransferred', [
            'domain'          => $domain,
            'domain_id'       => $domain->id,
            'client_id'       => $domain->client_id,
            'domain_name'     => $domain->domain,
            'transfer_date'   => now()->format('d.m.Y'),
            'action_taken'    => $actionTaken,
            'cancelled_count' => $openInvoices->count(),
        ]);

        Log::info('PRC-775: domain transfer-out handled', [
            'domain_id' => $domain->id,
            'cancelled_invoices' => $openInvoices->pluck('id')->all(),
            'action' => $actionTaken,
        ]);
    }

    /**
     * Try to renew the domain via the registrar module.
     */
    protected function renewViaRegistrar(ClientDomain $domain): void
    {
        // Resolve via domain->module first — admin's selection wins over the
        // TLD's default registrar (mirrors provisioning/sync contract).
        $registrarName = $domain->getEffectiveModuleName();
        if (! $registrarName) {
            return;
        }

        if (! $this->registrarManager->has($registrarName)) {
            Log::warning('Domain lifecycle: registrar module not found for renewal', [
                'domain' => $domain->domain,
                'registrar' => $registrarName,
            ]);

            return;
        }

        try {
            $module = $this->registrarManager->get($registrarName);
            $domainParts = explode('.', $domain->domain);
            $sld = $domainParts[0];
            $tldExtension = implode('.', array_slice($domainParts, 1));

            // Include additional_fields so registrars requiring TLD extras
            // (e.g., authorization IDs) don't reject the auto-renewal.
            $extraFields = $domain->fields->pluck('value', 'field_key')->toArray();

            $result = $this->registrarManager->call($module, 'renewDomain', [
                'domain' => $domain->domain,
                'sld' => $sld,
                'tld' => $tldExtension,
                'period' => $domain->period ?: 1,
                'additional_fields' => $extraFields,
            ]);

            if (isset($result['status']) && ($result['status'] === 'success' || $result['status'] === true)) {
                Log::info('Domain lifecycle: registrar renewal successful', [
                    'domain' => $domain->domain,
                    'registrar' => $registrarName,
                ]);
            } else {
                Log::warning('Domain lifecycle: registrar renewal returned non-success', [
                    'domain' => $domain->domain,
                    'registrar' => $registrarName,
                    'result' => $result,
                ]);
            }
        } catch (\Exception $e) {
            Log::warning('Domain lifecycle: registrar renewal failed (will still activate domain)', [
                'domain' => $domain->domain,
                'registrar' => $registrarName,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Send a lifecycle notification email.
     */
    protected function sendLifecycleEmail(string $templateKey, ClientDomain $domain, array $vars): void
    {
        try {
            SendEmailJob::dispatch(
                $templateKey,
                'client_domain',
                $domain->id,
                $vars,
                $domain->client->language ?? 'tr',
                $domain->client->email ?? null
            );
        } catch (\Exception $e) {
            Log::warning('Domain lifecycle: failed to send email', [
                'template' => $templateKey,
                'domain_id' => $domain->id,
                'error' => $e->getMessage(),
            ]);
        }
    }
}
