<?php

namespace App\Http\Controllers\Portal;

use App\Http\Controllers\Controller;
use App\Models\ClientService;
use App\Models\CustomerProfile;
use App\Models\Domain;
use App\Models\Invoice;
use App\Models\Order;
use App\Services\WhmcsCreditService;
use App\Services\WhmcsMirrorService;
use App\Services\WhmcsProvisioningService;
use App\Services\PricingService;
use App\Services\ZtApiService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Symfony\Component\HttpKernel\Exception\HttpException;

class PortalController extends Controller
{
    public function __construct(
        private readonly WhmcsMirrorService $mirror,
        private readonly WhmcsProvisioningService $provisioning,
        private readonly ZtApiService $api,
        private readonly WhmcsCreditService $credits,
        private readonly PricingService $pricing,
    ) {}

    public function home(): RedirectResponse
    {
        return Auth::check()
            ? redirect()->route('client.dashboard')
            : redirect()->route('login');
    }

    public function dashboard()
    {
        $user = Auth::user();
        $this->refreshMirrorIfEnabled($user);
        $profile = CustomerProfile::where('user_id', $user->id)->first();

        try {
            $creditSnapshot = $this->credits->accountSnapshot($user, false);
        } catch (\Throwable $e) {
            report($e);
            $creditSnapshot = [
                'balance' => 0.0,
                'currency' => strtoupper((string) ($user->currency ?: config('zt.currency.default', 'USD'))),
                'unavailable' => true,
            ];
        }

        return view('client.dashboard', [
            'servicesCount' => ClientService::where('user_id', $user->id)->whereIn('status', ['active', 'synced'])->count(),
            'domainsCount' => Domain::where('user_id', $user->id)->count(),
            'unpaidInvoicesCount' => Invoice::where('user_id', $user->id)->whereIn('status', ['unpaid', 'payment_pending'])->count(),
            'recentOrders' => Order::where('user_id', $user->id)->latest()->limit(5)->get(),
            'recentInvoices' => Invoice::where('user_id', $user->id)->latest()->limit(5)->get(),
            'profile' => $profile,
            'creditSnapshot' => $creditSnapshot,
        ]);
    }

    public function services()
    {
        $user = Auth::user();
        $this->refreshMirrorIfEnabled($user, ['services']);

        return view('client.services', [
            'services' => ClientService::where('user_id', $user->id)->latest()->paginate(15),
        ]);
    }

    public function showService(ClientService $service)
    {
        $user = Auth::user();
        abort_unless((int) $service->user_id === (int) $user->id, 403);

        return view('client.service-detail', [
            'service' => $service,
        ]);
    }

    public function openServiceCpanel(ClientService $service): RedirectResponse
    {
        $user = Auth::user();
        abort_unless((int) $service->user_id === (int) $user->id, 403);
        $this->ensureServiceActive($service);

        $serviceId = (int) ($service->whmcs_service_id ?: $service->api_service_id);
        abort_if($serviceId <= 0, 404, 'This service is still being prepared. Please try again later.');

        return $this->redirectToWhmcsClientArea(
            $user,
            'clientarea.php?action=productdetails&id='.$serviceId.'&dosinglesignon=1'
        );
    }

    public function changeServicePassword(Request $request, ClientService $service): RedirectResponse
    {
        $user = Auth::user();
        abort_unless((int) $service->user_id === (int) $user->id, 403);
        $this->ensureServiceActive($service);

        $validated = $request->validate([
            'service_password' => [
                'required',
                'string',
                'confirmed',
                'min:8',
                'max:64',
                'regex:/[a-z]/',
                'regex:/[A-Z]/',
                'regex:/[0-9]/',
            ],
        ], [
            'service_password.confirmed' => 'The service password confirmation does not match.',
            'service_password.regex' => 'The service password must contain at least one uppercase letter, one lowercase letter, and one number.',
        ]);

        $serviceId = (int) ($service->whmcs_service_id ?: $service->api_service_id);
        abort_if($serviceId <= 0, 404, 'This service is still being prepared. Please try again later.');

        $result = $this->api->changeServicePassword($serviceId, $validated['service_password']);

        if (! ($result['ok'] ?? false)) {
            return back()->withErrors(['service_password' => 'The service password could not be changed. Please try again or contact support.']);
        }

        return back()->with('status', 'Service password changed successfully.');
    }

    public function upgradeService(ClientService $service)
    {
        $user = Auth::user();
        abort_unless((int) $service->user_id === (int) $user->id, 403);
        $this->ensureServiceActive($service);

        $serviceId = (int) ($service->whmcs_service_id ?: $service->api_service_id);
        abort_if($serviceId <= 0, 404, 'This service is still being prepared. Please try again later.');

        $products = $this->upgradeProductsFor($service);

        return view('client.service-upgrade', [
            'service' => $service,
            'products' => $products,
        ]);
    }

    public function submitUpgradeService(Request $request, ClientService $service): RedirectResponse
    {
        $user = Auth::user();
        abort_unless((int) $service->user_id === (int) $user->id, 403);
        $this->ensureServiceActive($service);

        $validated = $request->validate([
            'product_id' => ['required', 'integer', 'min:1'],
            'billing_cycle' => ['required', 'string', 'max:40'],
        ]);

        $serviceId = (int) ($service->whmcs_service_id ?: $service->api_service_id);
        abort_if($serviceId <= 0, 404, 'This service is still being prepared. Please try again later.');

        $allowedProductIds = collect($this->upgradeProductsFor($service))->pluck('pid')->map(fn ($pid) => (int) $pid)->all();
        if (! in_array((int) $validated['product_id'], $allowedProductIds, true)) {
            return back()->withErrors(['product_id' => 'This package is not available as an upgrade for this service.']);
        }

        $result = $this->api->upgradeProduct(
            serviceId: $serviceId,
            newProductId: (int) $validated['product_id'],
            billingCycle: (string) $validated['billing_cycle'],
            paymentMethod: (string) config('zt.whmcs.default_payment_method', 'banktransfer'),
        );

        if (! ($result['ok'] ?? false)) {
            return back()->withErrors(['upgrade' => 'The package change could not be started. Please try again or contact support.']);
        }

        try {
            $this->mirror->refreshUser($user, true, ['invoices', 'services']);
        } catch (\Throwable $e) {
            report($e);
        }

        $invoiceId = (int) data_get($result, 'response.invoiceid', 0);
        if ($invoiceId > 0) {
            $invoice = Invoice::where('user_id', $user->id)->where('whmcs_invoice_id', $invoiceId)->first();
            if ($invoice) {
                return redirect()->route('client.invoices.show', $invoice)->with('status', 'Upgrade order created. Please complete payment.');
            }
        }

        return redirect()->route('client.invoices')->with('status', 'Upgrade order created. Refresh invoices if the new invoice is not visible yet.');
    }

    public function domains()
    {
        $user = Auth::user();
        $this->refreshMirrorIfEnabled($user, ['domains']);

        return view('client.domains', [
            'domains' => Domain::where('user_id', $user->id)->latest()->paginate(15),
        ]);
    }

    public function showDomain(Domain $domain)
    {
        $user = Auth::user();
        abort_unless((int) $domain->user_id === (int) $user->id, 403);

        $this->refreshMirrorIfEnabled($user, ['domains']);
        $domain->refresh();

        $domainId = (int) ($domain->whmcs_domain_id ?: $domain->api_domain_id);
        $nameservers = collect(is_array($domain->nameservers) ? $domain->nameservers : [])
            ->map(fn ($value) => strtolower(rtrim(trim((string) $value), '.')))
            ->filter()
            ->values()
            ->all();
        $nameserverNotice = null;

        if ($domainId > 0 && strtolower(trim((string) $domain->status)) === 'active') {
            try {
                $result = $this->api->domainNameservers($domainId, true);

                if (($result['ok'] ?? false) && count($result['nameservers'] ?? []) >= 2) {
                    $nameservers = $result['nameservers'];
                    $payload = is_array($domain->whmcs_payload) ? $domain->whmcs_payload : [];
                    foreach (['ns1', 'ns2', 'ns3', 'ns4', 'ns5'] as $index => $key) {
                        $payload[$key] = $nameservers[$index] ?? null;
                    }

                    $domain->forceFill([
                        'nameservers' => $nameservers,
                        'whmcs_payload' => $payload,
                        'whmcs_synced_at' => now(),
                    ])->save();
                } else {
                    $nameserverNotice = 'Live nameservers could not be loaded from the registrar. The saved WHMCS values are shown when available.';
                }
            } catch (\Throwable $e) {
                report($e);
                $nameserverNotice = 'Live nameservers could not be loaded from the registrar. The saved WHMCS values are shown when available.';
            }
        }

        $renewalOptions = [];
        $renewalNotice = null;
        $renewalStatus = strtolower(trim((string) $domain->status));

        if (in_array($renewalStatus, ['active', 'expired'], true)) {
            try {
                $renewalOptions = $this->pricing->domainBillingOptions(
                    (string) $domain->domain_name,
                    'renew',
                    strtoupper((string) ($domain->currency ?: $user->currency ?: 'USD')),
                );

                if ($renewalOptions === []) {
                    $renewalNotice = 'Renewal pricing is not currently available for this domain. Please contact support.';
                }
            } catch (\Throwable $e) {
                report($e);
                $renewalNotice = 'Renewal pricing could not be loaded right now. Please try again later.';
            }
        }

        return view('client.domain-detail', [
            'domain' => $domain,
            'nameservers' => $nameservers,
            'nameserverNotice' => $nameserverNotice,
            'renewalOptions' => $renewalOptions,
            'renewalNotice' => $renewalNotice,
            'pendingRenewalInvoice' => $this->findOpenDomainRenewalInvoice((int) $user->id, (string) $domain->domain_name),
        ]);
    }

    public function renewDomain(Request $request, Domain $domain): RedirectResponse
    {
        $user = Auth::user();
        abort_unless((int) $domain->user_id === (int) $user->id, 403);

        $this->refreshMirrorIfEnabled($user, ['domains']);
        $domain->refresh();

        $status = strtolower(trim((string) $domain->status));
        if (! in_array($status, ['active', 'expired'], true)) {
            throw new HttpException(409, 'This domain cannot be renewed in its current status.');
        }

        $validated = $request->validate([
            'years' => ['required', 'integer', 'min:1', 'max:10'],
        ]);

        $currency = strtoupper((string) ($domain->currency ?: $user->currency ?: 'USD'));

        try {
            $renewalOptions = $this->pricing->domainBillingOptions(
                (string) $domain->domain_name,
                'renew',
                $currency,
            );
        } catch (\Throwable $e) {
            report($e);
            return back()->withErrors(['renewal' => 'Renewal pricing could not be loaded. Please try again later.']);
        }

        $years = (int) $validated['years'];

        if (! array_key_exists($years, $renewalOptions)) {
            return back()->withErrors(['years' => 'The selected renewal period is not available for this domain.']);
        }

        $clientId = (int) ($user->whmcs_client_id ?? 0);
        abort_if($clientId <= 0, 404, 'Your account is not linked with WHMCS yet. Please contact support.');

        $domainId = (int) ($domain->whmcs_domain_id ?: $domain->api_domain_id);
        abort_if($domainId <= 0, 404, 'Domain renewal is not available yet. Please try again later.');

        $lock = Cache::lock('zt-domain-renewal:'.$user->id.':'.$domainId, 20);

        try {
            return $lock->block(3, function () use ($user, $domain, $clientId, $years) {
                // Refresh first so an already-created unpaid renewal invoice is
                // reused instead of creating a duplicate WHMCS order.
                try {
                    $this->mirror->refreshUser($user, true, ['invoices']);
                } catch (\Throwable $e) {
                    report($e);
                }

                $existingInvoice = $this->findOpenDomainRenewalInvoice((int) $user->id, (string) $domain->domain_name);
                if ($existingInvoice) {
                    return redirect()->route('client.invoices.show', $existingInvoice)
                        ->with('status', 'A renewal invoice already exists for this domain. Complete its payment to continue.');
                }

                $payload = is_array($domain->whmcs_payload) ? $domain->whmcs_payload : [];
                $paymentMethod = trim((string) data_get($payload, 'paymentmethod', ''));
                $defaultPaymentMethod = trim((string) config('zt.whmcs.default_payment_method', 'banktransfer'));
                $availablePaymentMethods = collect($this->api->paymentMethods())
                    ->pluck('slug')
                    ->map(fn ($method) => trim((string) $method))
                    ->filter()
                    ->values()
                    ->all();

                if ($paymentMethod === '' || ($availablePaymentMethods !== [] && ! in_array($paymentMethod, $availablePaymentMethods, true))) {
                    $paymentMethod = in_array($defaultPaymentMethod, $availablePaymentMethods, true)
                        ? $defaultPaymentMethod
                        : ($availablePaymentMethods[0] ?? $defaultPaymentMethod);
                }

                $result = $this->api->createDomainRenewalOrder(
                    clientId: $clientId,
                    domain: (string) $domain->domain_name,
                    years: $years,
                    paymentMethod: $paymentMethod,
                );

                if (! ($result['ok'] ?? false)) {
                    $message = trim((string) ($result['message'] ?? 'The renewal order could not be created.'));
                    return back()->withErrors(['renewal' => Str::limit($message, 240)]);
                }

                $invoiceId = (int) data_get($result, 'response.invoiceid', 0);
                if ($invoiceId <= 0) {
                    return back()->withErrors(['renewal' => 'WHMCS created the renewal order but did not return an invoice. Please contact support before trying again.']);
                }

                try {
                    $this->mirror->refreshUser($user, true, ['invoices']);
                } catch (\Throwable $e) {
                    report($e);
                }

                $invoice = Invoice::where('user_id', $user->id)
                    ->where('whmcs_invoice_id', $invoiceId)
                    ->first();

                if ($invoice) {
                    return redirect()->route('client.invoices.show', $invoice)
                        ->with('status', 'Domain renewal invoice created. Complete payment in this portal to renew the domain.');
                }

                return redirect()->route('client.invoices')
                    ->with('status', 'Domain renewal invoice created. Refresh invoices if it is not visible yet.');
            });
        } catch (\Illuminate\Contracts\Cache\LockTimeoutException) {
            return back()->withErrors(['renewal' => 'A renewal request is already being processed. Please wait a few seconds and try again.']);
        }
    }

    public function updateDomainNameservers(Request $request, Domain $domain): RedirectResponse
    {
        $user = Auth::user();
        abort_unless((int) $domain->user_id === (int) $user->id, 403);

        if (strtolower(trim((string) $domain->status)) !== 'active') {
            throw new HttpException(409, 'This domain is not active yet. Nameservers can be changed after activation.');
        }

        $validated = $request->validate([
            'ns1' => ['required', 'string', 'max:253', 'regex:/^(?=.{1,253}\.?$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}\.?$/i'],
            'ns2' => ['required', 'string', 'max:253', 'different:ns1', 'regex:/^(?=.{1,253}\.?$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}\.?$/i'],
            'ns3' => ['nullable', 'string', 'max:253', 'regex:/^(?=.{1,253}\.?$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}\.?$/i'],
            'ns4' => ['nullable', 'string', 'max:253', 'regex:/^(?=.{1,253}\.?$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}\.?$/i'],
            'ns5' => ['nullable', 'string', 'max:253', 'regex:/^(?=.{1,253}\.?$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}\.?$/i'],
        ], [
            'ns1.regex' => 'Nameserver 1 must be a valid hostname, for example ns1.example.com.',
            'ns2.regex' => 'Nameserver 2 must be a valid hostname, for example ns2.example.com.',
            'ns2.different' => 'Nameserver 2 must be different from Nameserver 1.',
            'ns3.regex' => 'Nameserver 3 must be a valid hostname.',
            'ns4.regex' => 'Nameserver 4 must be a valid hostname.',
            'ns5.regex' => 'Nameserver 5 must be a valid hostname.',
        ]);

        $domainId = (int) ($domain->whmcs_domain_id ?: $domain->api_domain_id);
        abort_if($domainId <= 0, 404, 'Domain management is not available yet. Please try again later.');

        $nameservers = collect(['ns1', 'ns2', 'ns3', 'ns4', 'ns5'])
            ->map(fn (string $key) => $validated[$key] ?? null)
            ->filter()
            ->values()
            ->all();

        $result = $this->api->updateDomainNameservers($domainId, $nameservers);

        if (! ($result['ok'] ?? false)) {
            $message = trim((string) ($result['message'] ?? 'The nameservers could not be changed.'));
            return back()->withInput()->withErrors(['nameservers' => Str::limit($message, 240)]);
        }

        $savedNameservers = $result['nameservers'] ?? $nameservers;
        $payload = is_array($domain->whmcs_payload) ? $domain->whmcs_payload : [];
        foreach (['ns1', 'ns2', 'ns3', 'ns4', 'ns5'] as $index => $key) {
            $payload[$key] = $savedNameservers[$index] ?? null;
        }

        $domain->forceFill([
            'nameservers' => $savedNameservers,
            'whmcs_payload' => $payload,
            'whmcs_synced_at' => now(),
        ])->save();

        return back()->with('status', 'Nameservers updated successfully. DNS propagation can take some time.');
    }

    public function invoices()
    {
        $user = Auth::user();

        if ((bool) config('zt.mirror.refresh_on_page_load', true)) {
            try {
                $this->mirror->refreshUser($user, true, ['invoices']);
            } catch (\Throwable $e) {
                report($e);
            }
        }

        return view('client.invoices', [
            'invoices' => Invoice::where('user_id', $user->id)->latest()->paginate(15),
        ]);
    }

    public function refreshServices(): RedirectResponse
    {
        $result = $this->mirror->refreshUser(Auth::user(), true, ['services']);

        return back()->with('status', $this->refreshMessage($result, 'services'));
    }

    public function openServiceWhmcs(ClientService $service): RedirectResponse
    {
        $user = Auth::user();
        abort_unless((int) $service->user_id === (int) $user->id, 403);
        $this->ensureServiceActive($service);

        $serviceId = (int) ($service->whmcs_service_id ?: $service->api_service_id);
        abort_if($serviceId <= 0, 404, 'This service is still being prepared. Please try again later.');

        return $this->redirectToWhmcsClientArea(
            $user,
            'clientarea.php?action=productdetails&id='.$serviceId
        );
    }

    public function refreshDomains(): RedirectResponse
    {
        $result = $this->mirror->refreshUser(Auth::user(), true, ['domains']);

        return back()->with('status', $this->refreshMessage($result, 'domains'));
    }

    public function refreshInvoices(): RedirectResponse
    {
        $user = Auth::user();
        $result = $this->mirror->refreshUser($user, true, ['invoices']);
        $checked = 0;
        $attention = 0;

        // The explicit Refresh action also verifies provisioning for Paid
        // invoices. It accepts Pending orders and, when configured, repairs exact
        // paid invoice items that WHMCS still reports as Pending.
        if ((bool) config('zt.whmcs.reconcile_paid_provisioning_on_manual_refresh', true)) {
            $paidInvoices = Invoice::where('user_id', $user->id)
                ->where(function ($query) {
                    $query->where('status', 'paid')->orWhereRaw('LOWER(whmcs_status) = ?', ['paid']);
                })
                ->latest()
                ->limit((int) config('zt.whmcs.reconcile_paid_invoice_limit', 5))
                ->get();

            foreach ($paidInvoices as $invoice) {
                try {
                    $report = $this->provisioning->reconcilePaidInvoice(
                        $user,
                        $invoice,
                        (bool) config('zt.whmcs.force_provision_paid_pending_items', true),
                        false,
                    );
                    $checked++;
                    if (! ($report['ok'] ?? false)) {
                        $attention++;
                    }
                } catch (\Throwable $e) {
                    report($e);
                    $attention++;
                }
            }

            $this->mirror->refreshUser($user, true, ['services', 'domains']);
        }

        $message = $this->refreshMessage($result, 'invoices');
        if ($checked > 0) {
            $message .= ' Order status checked for '.$checked.' paid invoice(s).';
        }
        if ($attention > 0) {
            $message .= ' '.$attention.' invoice(s) are still being finalized. Please try refreshing again later or contact support.';
        }

        return back()->with('status', $message);
    }

    public function support()
    {
        return view('client.support');
    }


    protected function upgradeProductsFor(ClientService $service): array
    {
        $payload = is_array($service->whmcs_payload) ? $service->whmcs_payload : [];
        $currentPid = (int) ($service->api_pid ?: data_get($payload, 'pid', 0));
        $currentGroupId = (int) data_get($payload, 'gid', 0);
        $currentGroupName = Str::lower(trim((string) data_get($payload, 'groupname', '')));
        $serviceFamily = $this->hostingProductFamily(implode(' ', array_filter([
            $service->name,
            $service->domain_name,
            $currentGroupName,
            data_get($payload, 'productname'),
            data_get($payload, 'type'),
        ])));

        return collect($this->api->products())
            ->filter(function ($product) use ($currentPid, $currentGroupId, $currentGroupName, $serviceFamily) {
                $pid = (int) data_get($product, 'pid', 0);
                if ($pid <= 0 || $pid === $currentPid) {
                    return false;
                }

                $productGroupId = (int) data_get($product, 'gid', 0);
                if ($currentGroupId > 0 && $productGroupId > 0) {
                    return $productGroupId === $currentGroupId;
                }

                $productGroupName = Str::lower(trim((string) data_get($product, 'groupname', '')));
                if ($currentGroupName !== '' && $productGroupName !== '') {
                    return $productGroupName === $currentGroupName;
                }

                $productFamily = $this->hostingProductFamily(implode(' ', array_filter([
                    data_get($product, 'name'),
                    $productGroupName,
                    data_get($product, 'description'),
                    data_get($product, 'type'),
                ])));

                return $serviceFamily !== null && $productFamily === $serviceFamily;
            })
            ->values()
            ->all();
    }

    protected function hostingProductFamily(string $value): ?string
    {
        $value = Str::lower(strip_tags($value));

        $families = [
            'dedicated' => ['dedicated', 'bare metal'],
            'vps' => ['vps', 'virtual private', 'cloud server', 'cloud hosting'],
            'reseller' => ['reseller'],
            'wordpress' => ['wordpress', 'wp hosting'],
            'email' => ['email hosting', 'e-mail hosting', 'mail hosting'],
            'shared' => ['shared', 'web hosting', 'cpanel hosting', 'business hosting'],
        ];

        foreach ($families as $family => $keywords) {
            foreach ($keywords as $keyword) {
                if (Str::contains($value, $keyword)) {
                    return $family;
                }
            }
        }

        return null;
    }

    protected function findOpenDomainRenewalInvoice(int $userId, string $domainName): ?Invoice
    {
        $domainName = strtolower(trim($domainName));

        if ($domainName === '') {
            return null;
        }

        return Invoice::query()
            ->where('user_id', $userId)
            ->whereNotIn('status', ['paid', 'cancelled', 'refunded'])
            ->whereHas('items', function ($query) use ($domainName) {
                $query->whereRaw('LOWER(description) LIKE ?', ['%'.str_replace(['%', '_'], ['\%', '\_'], $domainName).'%'])
                    ->where(function ($renewalQuery) {
                        $renewalQuery->whereRaw('LOWER(description) LIKE ?', ['%renew%'])
                            ->orWhereRaw('LOWER(description) LIKE ?', ['%extension%']);
                    });
            })
            ->latest('id')
            ->first();
    }

    protected function refreshMirrorIfEnabled($user, array $modules = ['invoices', 'services', 'domains']): void
    {
        if (! (bool) config('zt.mirror.refresh_on_page_load', true)) {
            return;
        }

        try {
            $this->mirror->refreshUser($user, false, $modules);
        } catch (\Throwable $e) {
            report($e);
        }
    }

    protected function redirectToWhmcsClientArea($user, string $path): RedirectResponse
    {
        $clientId = (int) ($user->whmcs_client_id ?? 0);
        abort_if($clientId <= 0, 404, 'Account management is not available yet. Please contact support.');

        $result = $this->api->createSsoTokenCustomRedirect(
            $clientId,
            $path,
            $user->whmcs_user_id ? (int) $user->whmcs_user_id : null,
        );

        if (! ($result['ok'] ?? false) || empty($result['redirect_url'])) {
            throw new HttpException(502, 'The requested account page could not be opened. Please try again or contact support.');
        }

        return redirect()->away($this->absoluteWhmcsRedirectUrl((string) $result['redirect_url']));
    }

    protected function absoluteWhmcsRedirectUrl(string $url): string
    {
        $url = trim($url);
        $clientAreaUrl = $this->whmcsClientAreaUrl();

        if ($clientAreaUrl !== '') {
            $parts = parse_url($url);

            if (is_array($parts) && (($parts['path'] ?? '') === '/oauth/singlesignon.php')) {
                $query = isset($parts['query']) && $parts['query'] !== '' ? '?'.$parts['query'] : '';
                return rtrim($clientAreaUrl, '/').'/oauth/singlesignon.php'.$query;
            }

            if ($url !== '' && ! preg_match('#^https?://#i', $url)) {
                return rtrim($clientAreaUrl, '/').'/'.ltrim($url, '/');
            }
        }

        return $url;
    }

    protected function whmcsClientAreaUrl(): string
    {
        $configured = trim((string) config('zt.whmcs.client_area_url', ''));
        if ($configured !== '') {
            return rtrim($configured, '/');
        }

        $apiUrl = trim((string) config('zt.api_base_url', ''));
        if ($apiUrl === '') {
            return '';
        }

        $parts = parse_url($apiUrl);
        if (! is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) {
            return '';
        }

        $base = $parts['scheme'].'://'.$parts['host'];
        if (! empty($parts['port'])) {
            $base .= ':'.$parts['port'];
        }

        $path = preg_replace('#/includes/api\.php$#', '', (string) ($parts['path'] ?? '')) ?? '';

        return rtrim($base.rtrim($path, '/'), '/');
    }

    protected function ensureServiceActive(ClientService $service): void
    {
        $status = strtolower(trim((string) $service->status));

        if ($status !== 'active') {
            throw new HttpException(409, 'This service is not active yet. Management tools will be available after activation.');
        }
    }

    protected function refreshMessage(array $result, string $module): string
    {
        if ($result['skipped'] ?? false) {
            return 'Nothing needed to be refreshed.';
        }

        $moduleResult = $result['modules'][$module] ?? null;
        if (is_array($moduleResult)) {
            return ucfirst($module).' updated successfully.';
        }

        return ($result['ok'] ?? false) ? 'Information updated successfully.' : 'Refresh completed with errors.';
    }
}
