<?php

use App\Http\Controllers\Admin\BrandingSettingsController;
use App\Http\Controllers\Admin\DashboardController;
use App\Http\Controllers\Admin\AffiliateMonitorController;
use App\Http\Controllers\Admin\MarginSettingsController;
use App\Http\Controllers\Admin\ManualStockController;
use App\Http\Controllers\Admin\ProviderAvailabilityController;
use App\Http\Controllers\Admin\PromoCodeController;
use App\Http\Controllers\Admin\ProductSettingsController;
use App\Http\Controllers\Admin\SecurityMonitorController;
use App\Http\Controllers\Admin\TransactionMonitorController;
use App\Http\Controllers\AccountIssueReportController;
use App\Http\Controllers\CheckoutController;
use App\Http\Controllers\DuitkuCallbackController;
use App\Http\Controllers\DuitkuPaymentMethodController;
use App\Http\Controllers\LeaderboardController;
use App\Http\Controllers\LyvaflowWhatsappWebhookController;
use App\Http\Controllers\LyvaCoinController;
use App\Http\Controllers\SupportChatController;
use App\Http\Controllers\TransactionHistoryController;
use App\Http\Controllers\VipaymentNicknameController;
use App\Http\Controllers\VipaymentProductServiceController;
use App\Models\Transaction;
use App\Services\DuitkuService;
use App\Services\VipaymentService;
use App\Support\MobileCatalogArtworkResolver;
use App\Support\MobileCatalogCategoryResolver;
use App\Support\ProductFamilyMatcher;
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

Route::get('/', function (VipaymentService $vipayment) {
    $vipCatalogProducts = [];

    if ($vipayment->configured()) {
        try {
            $vipCatalogProducts = $vipayment->getCatalogProducts();
        } catch (\Throwable $exception) {
            report($exception);
        }
    }

    return Inertia::render('Welcome', [
        'vipCatalogProducts' => $vipCatalogProducts,
    ]);
})->name('home');

Route::get('/download-aplikasi', function () {
    $apkPath = public_path('apk/lyvaindonesia-latest.apk');

    if (! file_exists($apkPath)) {
        return response(
            <<<'HTML'
<!doctype html>
<html lang="id">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>APK belum tersedia</title>
    <style>
        body { font-family: Arial, sans-serif; background: #081120; color: #f8fbff; margin: 0; padding: 32px; }
        .card { max-width: 640px; margin: 48px auto; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.1); border-radius: 24px; padding: 28px; }
        h1 { margin: 0 0 12px; font-size: 28px; }
        p { color: #d4deef; line-height: 1.7; }
        code { background: rgba(255,255,255,0.08); padding: 2px 6px; border-radius: 8px; }
        a { color: #7ef3d7; }
    </style>
</head>
<body>
    <div class="card">
        <h1>APK Android belum tersedia</h1>
        <p>File aplikasi belum diupload ke server. Upload dulu file APK ke <code>public/apk/lyvaindonesia-latest.apk</code>, lalu buka halaman ini lagi.</p>
        <p><a href="/">Kembali ke beranda Lyva Indonesia</a></p>
    </div>
</body>
</html>
HTML,
            503,
            ['Content-Type' => 'text/html; charset=UTF-8'],
        );
    }

    return response()->download($apkPath, 'lyvaindonesia-latest.apk', [
        'Content-Type' => 'application/vnd.android.package-archive',
        'Cache-Control' => 'public, max-age=60',
    ]);
})->name('android.download');


Route::options('mobile/catalog', function () {
    return response('', 204)->withHeaders([
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
    ]);
})->name('mobile.catalog.options');

Route::get('mobile/catalog', function (VipaymentService $vipayment) {
    $headers = [
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
        'Cache-Control' => 'public, max-age=300',
    ];

    if (! $vipayment->configured()) {
        return response()->json([
            'message' => 'VIPayment belum dikonfigurasi.',
            'data' => [],
            'meta' => [
                'count' => 0,
                'source' => 'fallback',
                'generatedAt' => now()->toIso8601String(),
            ],
        ], 503)->withHeaders($headers);
    }

    try {
        $products = collect($vipayment->getCatalogProducts())
            ->filter(fn (array $product) => filled($product['id'] ?? null) && filled($product['name'] ?? null))
            ->map(function (array $product) {
                $productId = (string) ($product['id'] ?? '');
                $categoryOverride = MobileCatalogCategoryResolver::resolve($productId);
                $artwork = MobileCatalogArtworkResolver::resolve($productId);

                return [
                    'id' => $productId,
                    'name' => (string) ($product['name'] ?? ''),
                    'categoryId' => (string) ($categoryOverride['categoryId'] ?? ($product['categoryId'] ?? 'lainnya')),
                    'categoryTitle' => (string) ($categoryOverride['categoryTitle'] ?? ($product['categoryTitle'] ?? 'Lainnya')),
                    'sortOrder' => (int) ($categoryOverride['sortOrder'] ?? 999999),
                    'badge' => filled($product['badge'] ?? null) ? (string) $product['badge'] : null,
                    'coverImage' => $artwork['coverImage'],
                    'iconImage' => $artwork['iconImage'],
                    'detailUrl' => route('products.show', ['product' => $productId]),
                ];
            })
            ->sortBy([
                ['sortOrder', 'asc'],
                ['name', 'asc'],
            ])
            ->values()
            ->all();

        return response()->json([
            'message' => 'Produk berhasil diambil dari katalog Lyva.',
            'data' => $products,
            'meta' => [
                'count' => count($products),
                'source' => 'vipayment',
                'generatedAt' => now()->toIso8601String(),
            ],
        ])->withHeaders($headers);
    } catch (\Throwable $exception) {
        report($exception);

        return response()->json([
            'message' => $exception->getMessage() ?: 'Gagal mengambil katalog produk.',
            'data' => [],
            'meta' => [
                'count' => 0,
                'source' => 'fallback',
                'generatedAt' => now()->toIso8601String(),
            ],
        ], 502)->withHeaders($headers);
    }
})->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:vipayment-services'])->name('mobile.catalog');

Route::options('mobile/payments', function () {
    return response('', 204)->withHeaders([
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
    ]);
})->middleware(\App\Http\Middleware\AllowMobileCors::class)->name('mobile.payments.options');

Route::get('mobile/payments', function (\Illuminate\Http\Request $request, DuitkuService $duitku) {
    $headers = [
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
        'Cache-Control' => 'public, max-age=120',
    ];

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

    if (! $duitku->configured()) {
        return response()->json([
            'message' => 'Duitku belum dikonfigurasi.',
            'data' => [],
        ], 503)->withHeaders($headers);
    }

    try {
        return response()->json([
            'message' => 'Metode pembayaran berhasil diambil.',
            'data' => $duitku->getPaymentMethods((int) $validated['amount']),
            'meta' => [
                'amount' => (int) $validated['amount'],
                'generatedAt' => now()->toIso8601String(),
            ],
        ])->withHeaders($headers);
    } catch (\Throwable $exception) {
        report($exception);

        return response()->json([
            'message' => $exception->getMessage() ?: 'Gagal mengambil metode pembayaran.',
            'data' => [],
        ], 502)->withHeaders($headers);
    }
})->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:30,1'])->name('mobile.payments');

Route::options('mobile/products/{product}', function () {
    return response('', 204)->withHeaders([
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
    ]);
})->middleware(\App\Http\Middleware\AllowMobileCors::class)->name('mobile.products.show.options');

Route::get('mobile/products/{product}', function (string $product, VipaymentService $vipayment, DuitkuService $duitku) {
    $headers = [
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
        'Cache-Control' => 'public, max-age=180',
    ];

    if (! $vipayment->configured()) {
        return response()->json([
            'message' => 'VIPayment belum dikonfigurasi.',
        ], 503)->withHeaders($headers);
    }

    try {
        $canonicalAliases = [
            'vidio' => 'vip-game-vidio-premier',
            'vidio-premier' => 'vip-game-vidio-premier',
            'iqiyi' => 'vip-game-iqiyi-premium',
            'iqiyi-premium' => 'vip-game-iqiyi-premium',
            'viu' => 'vip-game-viu-premium',
            'viu-premium' => 'vip-game-viu-premium',
            'youtube-premium' => 'vip-game-youtube-premium',
        ];

        $canonicalProductId = $canonicalAliases[$product] ?? null;

        if ($canonicalProductId === null) {
            $currentProductServices = null;

            try {
                $currentProductServices = $vipayment->getProductServices($product);
            } catch (\Throwable $exception) {
                report($exception);
            }

            if ($currentProductServices === null || $currentProductServices === []) {
                $canonicalProductId = collect([
                    'vip-game-'.$product,
                    'vip-prepaid-'.$product,
                ])->first(function (string $candidate) use ($vipayment, $product) {
                    if ($candidate === $product) {
                        return false;
                    }

                    return $vipayment->findCatalogProduct($candidate) !== null;
                });
            }
        }

        if (is_string($canonicalProductId) && $canonicalProductId !== '') {
            $product = $canonicalProductId;
        }

        if (! $vipayment->isProductPubliclyAvailable($product)) {
            return response()->json([
                'message' => 'Produk tidak tersedia untuk publik.',
            ], 404)->withHeaders($headers);
        }

        $vipCatalogProduct = $vipayment->findCatalogProduct($product);

        if (! is_array($vipCatalogProduct)) {
            $resolvedCatalogId = collect([
                $product,
                'vip-game-'.$product,
                'vip-prepaid-'.$product,
            ])->first(fn (string $candidate) => is_array($vipayment->findCatalogProduct($candidate)));

            if (is_string($resolvedCatalogId) && $resolvedCatalogId !== '' && $resolvedCatalogId !== $product) {
                $product = $resolvedCatalogId;
                $vipCatalogProduct = $vipayment->findCatalogProduct($product);
            }
        }

        if (! is_array($vipCatalogProduct)) {
            return response()->json([
                'message' => 'Produk tidak ditemukan di katalog.',
            ], 404)->withHeaders($headers);
        }

        $initialVipaymentTabs = [];
        $initialVipaymentSource = 'fallback';
        $initialVipaymentMessage = '';
        $initialPaymentMethods = [];
        $initialPaymentMethodsAmount = null;

        try {
            $services = $vipayment->getProductServices($product);

            if (is_array($services) && $services !== []) {
                $initialVipaymentTabs = $services;
                $initialVipaymentSource = 'vipayment';
                $initialPaymentMethodsAmount = collect($services)
                    ->flatMap(fn (array $tab) => $tab['groups'] ?? [])
                    ->flatMap(fn (array $group) => $group['options'] ?? [])
                    ->map(fn (array $option) => (int) ($option['price'] ?? 0))
                    ->first(fn (int $price) => $price > 0);
            } elseif ($services === null) {
                $initialVipaymentMessage = 'Produk ini belum punya mapping VIPayment. Data lokal tetap dipakai.';
            } else {
                $initialVipaymentMessage = 'VIPayment belum mengembalikan layanan untuk produk ini. Data lokal tetap dipakai.';
            }
        } catch (\Throwable $exception) {
            report($exception);
            $initialVipaymentMessage = $exception->getMessage() ?: 'Gagal mengambil data produk dari VIPayment.';
        }

        if ($initialPaymentMethodsAmount && $duitku->configured()) {
            try {
                $initialPaymentMethods = $duitku->getPaymentMethods($initialPaymentMethodsAmount);
            } catch (\Throwable $exception) {
                report($exception);
            }
        }

        $resolvedProductName = (string) ($vipCatalogProduct['name'] ?? '');
        $productFamily = ProductFamilyMatcher::resolve($product, $resolvedProductName);
        $completedTransactionsQuery = ProductFamilyMatcher::applyTransactionScope(
            Transaction::query()->where('status', Transaction::STATUS_COMPLETED),
            $product,
            $resolvedProductName,
        );

        $ratingAggregate = (clone $completedTransactionsQuery)
            ->whereNotNull('rating_score')
            ->selectRaw(
                'count(*) as total_reviews, avg(rating_score) as average_score, '
                .'sum(case when rating_score >= 4 then 1 else 0 end) as recommended_reviews, '
                .'sum(case when rating_score = 5 then 1 else 0 end) as five_star_reviews'
            )
            ->first();

        $processingDurations = (clone $completedTransactionsQuery)
            ->whereNotNull('fulfilled_at')
            ->get(['created_at', 'paid_at', 'fulfilled_at'])
            ->map(static function (Transaction $transaction): ?int {
                $startedAt = $transaction->paid_at ?? $transaction->created_at;
                $completedAt = $transaction->fulfilled_at;

                if (! $startedAt || ! $completedAt || $completedAt->lessThanOrEqualTo($startedAt)) {
                    return null;
                }

                return max(1, $startedAt->diffInMinutes($completedAt));
            })
            ->filter()
            ->values()
            ->all();

        $formatBuyerName = static function (?string $customerName, ?string $customerEmail, ?string $customerWhatsapp): string {
            $candidate = trim((string) ($customerName ?: $customerEmail ?: $customerWhatsapp ?: 'Pembeli'));

            if ($candidate === '') {
                return 'Pembeli';
            }

            if (filter_var($candidate, FILTER_VALIDATE_EMAIL)) {
                [$localPart] = explode('@', $candidate, 2);
                $candidate = $localPart ?: 'Pembeli';
            }

            $candidate = preg_replace('/\s+/', ' ', trim($candidate)) ?: 'Pembeli';

            if (mb_strlen($candidate) <= 2) {
                return mb_substr($candidate, 0, 1).'*';
            }

            return mb_substr($candidate, 0, 3).str_repeat('*', min(3, max(1, mb_strlen($candidate) - 3)));
        };

        $formatDurationLabel = static function (array $durations): string {
            if ($durations === []) {
                return 'Belum ada data';
            }

            sort($durations);

            $min = (int) $durations[0];
            $max = (int) $durations[(int) floor((count($durations) - 1) * 0.75)];

            $format = static function (int $minutes): string {
                if ($minutes >= 60) {
                    $hours = (int) ceil($minutes / 60);

                    return $hours.' jam';
                }

                return $minutes.' menit';
            };

            if ($min === $max) {
                return $format($min);
            }

            return $format($min).'-'.$format($max);
        };

        $ratedTransactions = (clone $completedTransactionsQuery)
            ->whereNotNull('rating_score')
            ->orderByDesc('rated_at')
            ->orderByDesc('updated_at')
            ->limit(6)
            ->get([
                'public_id',
                'customer_name',
                'customer_email',
                'customer_whatsapp',
                'rating_score',
                'rating_comment',
                'rated_at',
                'updated_at',
            ]);

        $totalReviews = (int) ($ratingAggregate?->total_reviews ?? 0);
        $averageScore = $totalReviews > 0 ? (float) ($ratingAggregate?->average_score ?? 0) : 0;
        $recommendationRate = $totalReviews > 0 ? (int) round(((int) ($ratingAggregate?->recommended_reviews ?? 0) / $totalReviews) * 100) : 0;
        $fiveStarRate = $totalReviews > 0 ? (int) round(((int) ($ratingAggregate?->five_star_reviews ?? 0) / $totalReviews) * 100) : 0;
        $ratingTitle = $productFamily['title'] ?: ($resolvedProductName !== '' ? $resolvedProductName : Str::headline(str_replace('-', ' ', $product)));
        $categoryOverride = MobileCatalogCategoryResolver::resolve($product);
        $artwork = MobileCatalogArtworkResolver::resolve($product);
        $badge = filled($vipCatalogProduct['badge'] ?? null) ? (string) $vipCatalogProduct['badge'] : null;

        $productContext = Str::lower(trim(implode(' ', [
            (string) $product,
            $resolvedProductName !== '' ? $resolvedProductName : Str::headline(str_replace('-', ' ', $product)),
            (string) ($categoryOverride['categoryId'] ?? ($vipCatalogProduct['categoryId'] ?? '')),
            (string) ($categoryOverride['categoryTitle'] ?? ($vipCatalogProduct['categoryTitle'] ?? '')),
        ])));
        $accountStyleKeywords = ['chatgpt', 'capcut', 'canva', 'netflix', 'spotify', 'youtube', 'prime video', 'bstation', 'alight motion', 'viu', 'vidio'];
        $loginStyleKeywords = ['login', 'username', 'password', 'email / username', 'email/username'];
        $isLoginProduct = (($categoryOverride['categoryId'] ?? ($vipCatalogProduct['categoryId'] ?? '')) === 'login')
            || collect($loginStyleKeywords)->contains(fn (string $keyword): bool => Str::contains($productContext, $keyword));
        $isAccountStyleProduct = collect($accountStyleKeywords)->contains(fn (string $keyword): bool => Str::contains($productContext, $keyword));
        $trustBadges = $isLoginProduct
            ? ['Proses khusus login product', 'Diverifikasi sebelum kirim', 'Support bantu cek data']
            : ($isAccountStyleProduct
                ? ['Official supply & safe', 'Proses otomatis lebih cepat', 'Cocok untuk akun premium rutin']
                : ['Official supply & safe', 'Proses otomatis lebih cepat', 'Cocok untuk top up rutin']);
        $guideTitle = ($isAccountStyleProduct ? 'Cara aktifkan ' : 'Cara top up ')
            .($resolvedProductName !== '' ? $resolvedProductName : Str::headline(str_replace('-', ' ', $product)));
        $guideSteps = $isLoginProduct
            ? [
                'Pilih paket login atau nominal yang kamu butuhkan.',
                'Baca instruksi produk dan pastikan tipe paket yang dipilih sudah sesuai kebutuhan akun.',
                'Masukkan data akun sesuai instruksi produk dengan benar.',
                'Periksa lagi email, username, password, atau server sebelum lanjut checkout.',
                'Pilih metode pembayaran yang tersedia di aplikasi.',
                'Selesaikan pembayaran lalu pesanan akan diverifikasi dan diproses.',
            ]
            : ($isAccountStyleProduct
                ? [
                    'Pilih paket atau durasi akun yang kamu inginkan.',
                    'Pastikan layanan yang dipilih cocok dengan kebutuhan akun atau langgananmu.',
                    'Masukkan email, username, atau detail akun sesuai kebutuhan layanan.',
                    'Cek kembali data akun agar aktivasi tidak tertunda.',
                    'Pilih metode pembayaran yang tersedia di aplikasi.',
                    'Lanjutkan pembayaran lalu pesanan akan diproses otomatis.',
                ]
                : [
                    'Pilih nominal atau paket yang kamu inginkan.',
                    'Pastikan nominal yang dipilih sudah sesuai kebutuhan top up akunmu.',
                    'Masukkan User ID, server, atau data akun dengan benar.',
                    'Cek kembali data akun tujuan sebelum melanjutkan pembayaran.',
                    'Pilih metode pembayaran yang tersedia di aplikasi.',
                    'Lanjutkan pembayaran dan item akan diproses otomatis.',
                ]);

        return response()->json([
            'message' => 'Detail produk berhasil diambil dari website Lyva.',
            'data' => [
                'product' => [
                    'id' => $product,
                    'name' => $resolvedProductName !== '' ? $resolvedProductName : Str::headline(str_replace('-', ' ', $product)),
                    'categoryId' => (string) ($categoryOverride['categoryId'] ?? ($vipCatalogProduct['categoryId'] ?? 'lainnya')),
                    'categoryTitle' => (string) ($categoryOverride['categoryTitle'] ?? ($vipCatalogProduct['categoryTitle'] ?? 'Lainnya')),
                    'badge' => $badge,
                    'coverImage' => $artwork['coverImage'],
                    'iconImage' => $artwork['iconImage'],
                    'detailUrl' => route('products.show', ['product' => $product]),
                ],
                'tabs' => $initialVipaymentTabs,
                'payments' => $initialPaymentMethods,
                'guide' => [
                    'title' => $guideTitle,
                    'steps' => $guideSteps,
                    'trustBadges' => $trustBadges,
                ],
                'nicknameLookup' => $vipayment->nicknameLookupConfig($product),
                'ratings' => [
                    'summary' => [
                        'average' => number_format($averageScore, 2, '.', ''),
                        'totalReviews' => $totalReviews,
                        'recommendationRate' => $recommendationRate,
                        'fiveStarRate' => $fiveStarRate,
                        'processingSpeed' => $formatDurationLabel($processingDurations),
                        'familyTitle' => $ratingTitle,
                    ],
                    'reviews' => $ratedTransactions->map(static function (Transaction $transaction) use ($formatBuyerName): array {
                        $comment = trim((string) $transaction->rating_comment);

                        return [
                            'id' => $transaction->public_id,
                            'name' => $formatBuyerName($transaction->customer_name, $transaction->customer_email, $transaction->customer_whatsapp),
                            'badge' => 'Verified Buyer',
                            'timeLabel' => ($transaction->rated_at ?? $transaction->updated_at)?->locale('id')->translatedFormat('d M Y, H:i') ?? '-',
                            'rating' => (int) ($transaction->rating_score ?? 0),
                            'comment' => $comment !== '' ? $comment : 'Pesanan diproses lancar, produk masuk sesuai pilihan, dan checkout terasa mudah dipakai.',
                        ];
                    })->all(),
                ],
            ],
            'meta' => [
                'source' => $initialVipaymentSource,
                'message' => $initialVipaymentMessage,
                'generatedAt' => now()->toIso8601String(),
            ],
        ])->withHeaders($headers);
    } catch (\Throwable $exception) {
        report($exception);

        return response()->json([
            'message' => $exception->getMessage() ?: 'Gagal mengambil detail produk.',
        ], 502)->withHeaders($headers);
    }
})->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:vipayment-services'])->name('mobile.products.show');

Route::options('mobile/products/{product}/nickname', function () {
    return response('', 204)->withHeaders([
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'POST, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
    ]);
})->middleware(\App\Http\Middleware\AllowMobileCors::class)->name('mobile.products.nickname.options');

Route::post('mobile/products/{product}/nickname', function (\Illuminate\Http\Request $request, string $product, VipaymentService $vipayment) {
    $headers = [
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'POST, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
    ];

    $validated = $request->validate([
        'target' => ['required', 'string', 'max:120'],
        'zone' => ['nullable', 'string', 'max:120'],
    ]);

    if (! $vipayment->configured()) {
        return response()->json([
            'status' => 'unavailable',
            'message' => 'VIPayment belum dikonfigurasi.',
        ], 503)->withHeaders($headers);
    }

    if (! $vipayment->supportsNicknameLookup($product)) {
        return response()->json([
            'status' => 'unsupported',
            'message' => 'Cek username belum tersedia untuk produk ini.',
        ])->withHeaders($headers);
    }

    try {
        $nickname = $vipayment->lookupGameNickname(
            $product,
            (string) ($validated['target'] ?? ''),
            $validated['zone'] ?? null,
        );

        if (! is_string($nickname) || $nickname === '') {
            return response()->json([
                'status' => 'not_found',
                'message' => 'Username tidak ditemukan.',
            ])->withHeaders($headers);
        }

        return response()->json([
            'status' => 'success',
            'message' => 'Nickname berhasil ditemukan.',
            'nickname' => $nickname,
        ])->withHeaders($headers);
    } catch (\Throwable $exception) {
        report($exception);

        return response()->json([
            'status' => 'error',
            'message' => $exception->getMessage() ?: 'Gagal mengecek username.',
        ], 422)->withHeaders($headers);
    }
})->withoutMiddleware([ValidateCsrfToken::class])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:vipayment-nickname'])
    ->name('mobile.products.nickname');


Route::options('mobile/auth/{any?}', function () {
    return response('', 204)->withHeaders([
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'POST, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
    ]);
})->where('any', '.*')->middleware(\App\Http\Middleware\AllowMobileCors::class)
    ->name('mobile.auth.options');

Route::post('mobile/auth/register', [\App\Http\Controllers\MobileAuthController::class, 'register'])
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:6,1'])
    ->name('mobile.auth.register');
Route::post('mobile/auth/login', [\App\Http\Controllers\MobileAuthController::class, 'login'])
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:6,1'])
    ->name('mobile.auth.login');
Route::post('mobile/auth/google', [\App\Http\Controllers\MobileAuthController::class, 'google'])
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:10,1'])
    ->name('mobile.auth.google');
Route::post('mobile/auth/google/whatsapp', [\App\Http\Controllers\MobileAuthController::class, 'googleWhatsapp'])
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:10,1'])
    ->name('mobile.auth.google.whatsapp');
Route::post('mobile/auth/whatsapp/verify', [\App\Http\Controllers\MobileAuthController::class, 'verifyWhatsapp'])
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:6,1'])
    ->name('mobile.auth.whatsapp.verify');
Route::post('mobile/auth/whatsapp/resend', [\App\Http\Controllers\MobileAuthController::class, 'resendWhatsapp'])
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:6,1'])
    ->name('mobile.auth.whatsapp.resend');

Route::options('mobile/checkout/{any?}', function () {
    return response('', 204)->withHeaders([
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, POST, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin',
    ]);
})->where('any', '.*')->middleware(\App\Http\Middleware\AllowMobileCors::class)
    ->name('mobile.checkout.options');

Route::post('mobile/checkout', [\App\Http\Controllers\MobileCheckoutController::class, 'store'])
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:12,1'])
    ->name('mobile.checkout.store');
Route::get('mobile/checkout/{transaction}', [\App\Http\Controllers\MobileCheckoutController::class, 'show'])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:30,1'])
    ->name('mobile.checkout.show');

Route::options('mobile/account/{any?}', function () {
    return response('', 204)->withHeaders([
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, OPTIONS',
        'Access-Control-Allow-Headers' => 'Content-Type, Accept, Origin, Authorization, X-Lyva-Mobile-Token',
    ]);
})->where('any', '.*')->middleware(\App\Http\Middleware\AllowMobileCors::class)
    ->name('mobile.account.options');

Route::get('mobile/account/history', [\App\Http\Controllers\MobileAccountController::class, 'history'])
    ->middleware([\App\Http\Middleware\AllowMobileCors::class, 'throttle:30,1'])
    ->name('mobile.account.history');

Route::get('riwayat-transaksi', TransactionHistoryController::class)->name('transactions.history');

Route::get('leaderboard', LeaderboardController::class)->name('leaderboard');

Route::get('lyva-coins', LyvaCoinController::class)->name('coins.index');

Route::get('artikel', function () {
    return Inertia::render('Articles');
})->name('articles.index');

Route::get('terms-of-service', function () {
    return Inertia::render('TermsOfService');
})->name('legal.terms');

Route::get('privacy-policy', function () {
    return Inertia::render('PrivacyPolicy');
})->name('legal.privacy');

Route::middleware('auth')->group(function () {
Route::get('akun-bermasalah', function (VipaymentService $vipayment) {
    $accountIssueProducts = [
        ['id' => 'vip-game-chatgpt', 'name' => 'ChatGPT'],
        ['id' => 'vip-game-capcut-pro', 'name' => 'CapCut Pro'],
        ['id' => 'vip-game-canva-pro', 'name' => 'Canva Pro'],
        ['id' => 'vip-game-netflix', 'name' => 'Netflix'],
        ['id' => 'vip-game-spotify-premium', 'name' => 'Spotify Premium'],
        ['id' => 'vip-game-youtube-premium', 'name' => 'YouTube Premium'],
        ['id' => 'vip-game-bstation-premium', 'name' => 'Bstation Premium'],
        ['id' => 'vip-game-iqiyi-premium', 'name' => 'iQIYI Premium'],
        ['id' => 'vip-game-vidio-premier', 'name' => 'Vidio Premier'],
        ['id' => 'vip-game-viu-premium', 'name' => 'Viu Premium'],
        ['id' => 'vip-game-wetv-premium', 'name' => 'WeTV Premium'],
        ['id' => 'vip-game-vision-plus', 'name' => 'Vision Plus'],
    ];

    if ($vipayment->configured()) {
        try {
            $accountKeywords = ['chatgpt', 'capcut', 'canva', 'netflix', 'spotify', 'youtube', 'bstation', 'iqiyi', 'vidio', 'viu', 'wetv', 'vision plus'];

            $accountIssueProducts = collect($vipayment->getCatalogProducts())
                ->filter(function (array $product) use ($accountKeywords) {
                    $haystack = Str::lower(trim(($product['id'] ?? '').' '.($product['name'] ?? '').' '.($product['categoryId'] ?? '')));

                    return collect($accountKeywords)->contains(fn (string $keyword) => Str::contains($haystack, $keyword));
                })
                ->map(fn (array $product) => [
                    'id' => (string) ($product['id'] ?? ''),
                    'name' => (string) ($product['name'] ?? ''),
                ])
                ->filter(fn (array $product) => $product['id'] !== '' && $product['name'] !== '')
                ->unique('id')
                ->sortBy('name')
                ->values()
                ->all();
        } catch (\Throwable $exception) {
            report($exception);
        }
    }

    return Inertia::render('AccountIssues', [
        'products' => $accountIssueProducts,
    ]);
})->name('account-issues.index');
Route::post('akun-bermasalah', AccountIssueReportController::class)
    ->middleware('bot.guard')
    ->middleware('throttle:account-issues')
    ->name('account-issues.store');
});

Route::post('support/chat', SupportChatController::class)
    ->middleware('bot.guard')
    ->middleware('throttle:support-chat')
    ->name('support.chat.reply');

Route::post('lyvaflow/whatsapp/incoming', LyvaflowWhatsappWebhookController::class)
    ->middleware('throttle:lyvaflow-whatsapp-webhook')
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->name('lyvaflow.whatsapp.incoming');

Route::get('duitku/payment-methods', DuitkuPaymentMethodController::class)
    ->middleware('throttle:duitku-payment-methods')
    ->name('duitku.payment-methods');
Route::post('duitku/callback', DuitkuCallbackController::class)
    ->middleware('throttle:duitku-callback')
    ->withoutMiddleware([ValidateCsrfToken::class])
    ->name('duitku.callback');
Route::get('vipayment/products/{product}/services', VipaymentProductServiceController::class)
    ->middleware('throttle:vipayment-services')
    ->name('vipayment.products.services');
Route::post('vipayment/products/{product}/nickname', VipaymentNicknameController::class)
    ->middleware('throttle:vipayment-nickname')
    ->name('vipayment.products.nickname');
Route::post('checkout/preview', [CheckoutController::class, 'store'])
    ->middleware('bot.guard')
    ->middleware('throttle:checkout-preview')
    ->name('checkout.preview.store');
Route::post('checkout/promo', [CheckoutController::class, 'resolvePromo'])
    ->middleware('throttle:checkout-promo')
    ->name('checkout.promo.resolve');
Route::get('checkout/{transaction}', [CheckoutController::class, 'show'])->name('checkout.show');
Route::post('checkout/{transaction}/rating', [CheckoutController::class, 'rate'])
    ->middleware('throttle:checkout-rating')
    ->name('checkout.rating.store');

Route::get('produk/{product}', function (string $product, VipaymentService $vipayment, DuitkuService $duitku) {
    if ($vipayment->configured()) {
        try {
            $canonicalAliases = [
                'vidio' => 'vip-game-vidio-premier',
                'vidio-premier' => 'vip-game-vidio-premier',
                'iqiyi' => 'vip-game-iqiyi-premium',
                'iqiyi-premium' => 'vip-game-iqiyi-premium',
                'viu' => 'vip-game-viu-premium',
                'viu-premium' => 'vip-game-viu-premium',
                'youtube-premium' => 'vip-game-youtube-premium',
            ];

            $canonicalProductId = $canonicalAliases[$product] ?? null;

            if ($canonicalProductId === null) {
                $currentProductServices = null;

                try {
                    $currentProductServices = $vipayment->getProductServices($product);
                } catch (\Throwable $exception) {
                    report($exception);
                }

                if ($currentProductServices === null || $currentProductServices === []) {
                    $canonicalProductId = collect([
                        'vip-game-'.$product,
                        'vip-prepaid-'.$product,
                    ])->first(function (string $candidate) use ($vipayment, $product) {
                        if ($candidate === $product) {
                            return false;
                        }

                        return $vipayment->findCatalogProduct($candidate) !== null;
                    });
                }
            }

            if (is_string($canonicalProductId) && $canonicalProductId !== '') {
                if (request()->header('X-Inertia')) {
                    return Inertia::location(route('products.show', ['product' => $canonicalProductId]));
                }

                return to_route('products.show', ['product' => $canonicalProductId]);
            }
        } catch (\Throwable $exception) {
            report($exception);
        }
    }

    if (! $vipayment->isProductPubliclyAvailable($product)) {
        return to_route('home');
    }

    $vipCatalogProduct = null;
    $initialVipaymentTabs = [];
    $initialVipaymentSource = 'fallback';
    $initialVipaymentMessage = '';
    $initialPaymentMethods = [];
    $initialPaymentMethodsAmount = null;

    if ($vipayment->configured()) {
        try {
            $vipCatalogProduct = $vipayment->findCatalogProduct($product);
        } catch (\Throwable $exception) {
            report($exception);
        }

        try {
            $services = $vipayment->getProductServices($product);

            if (is_array($services) && $services !== []) {
                $initialVipaymentTabs = $services;
                $initialVipaymentSource = 'vipayment';
                $initialPaymentMethodsAmount = collect($services)
                    ->flatMap(fn (array $tab) => $tab['groups'] ?? [])
                    ->flatMap(fn (array $group) => $group['options'] ?? [])
                    ->map(fn (array $option) => (int) ($option['price'] ?? 0))
                    ->first(fn (int $price) => $price > 0);
            } elseif ($services === null) {
                $initialVipaymentMessage = 'Produk ini belum punya mapping VIPayment. Data lokal tetap dipakai.';
            } else {
                $initialVipaymentMessage = 'VIPayment belum mengembalikan layanan untuk produk ini. Data lokal tetap dipakai.';
            }
        } catch (\Throwable $exception) {
            report($exception);
            $initialVipaymentMessage = $exception->getMessage() ?: 'Gagal mengambil data produk dari VIPayment.';
        }
    }

    if ($initialPaymentMethodsAmount && $duitku->configured()) {
        try {
            $initialPaymentMethods = $duitku->getPaymentMethods($initialPaymentMethodsAmount);
        } catch (\Throwable $exception) {
            report($exception);
        }
    }

    $resolvedProductName = is_array($vipCatalogProduct) ? (string) ($vipCatalogProduct['name'] ?? '') : '';
    $productFamily = ProductFamilyMatcher::resolve($product, $resolvedProductName);
    $completedTransactionsQuery = ProductFamilyMatcher::applyTransactionScope(
        Transaction::query()->where('status', Transaction::STATUS_COMPLETED),
        $product,
        $resolvedProductName,
    );

    $ratingAggregate = (clone $completedTransactionsQuery)
        ->whereNotNull('rating_score')
        ->selectRaw(
            'count(*) as total_reviews, avg(rating_score) as average_score, '
            .'sum(case when rating_score >= 4 then 1 else 0 end) as recommended_reviews, '
            .'sum(case when rating_score = 5 then 1 else 0 end) as five_star_reviews'
        )
        ->first();

    $processingDurations = (clone $completedTransactionsQuery)
        ->whereNotNull('fulfilled_at')
        ->get(['created_at', 'paid_at', 'fulfilled_at'])
        ->map(static function (Transaction $transaction): ?int {
            $startedAt = $transaction->paid_at ?? $transaction->created_at;
            $completedAt = $transaction->fulfilled_at;

            if (! $startedAt || ! $completedAt || $completedAt->lessThanOrEqualTo($startedAt)) {
                return null;
            }

            return max(1, $startedAt->diffInMinutes($completedAt));
        })
        ->filter()
        ->values()
        ->all();

    $formatBuyerName = static function (?string $customerName, ?string $customerEmail, ?string $customerWhatsapp): string {
        $candidate = trim((string) ($customerName ?: $customerEmail ?: $customerWhatsapp ?: 'Pembeli'));

        if ($candidate === '') {
            return 'Pembeli';
        }

        if (filter_var($candidate, FILTER_VALIDATE_EMAIL)) {
            [$localPart] = explode('@', $candidate, 2);
            $candidate = $localPart ?: 'Pembeli';
        }

        $candidate = preg_replace('/\s+/', ' ', trim($candidate)) ?: 'Pembeli';

        if (mb_strlen($candidate) <= 2) {
            return mb_substr($candidate, 0, 1).'*';
        }

        return mb_substr($candidate, 0, 3).str_repeat('*', min(3, max(1, mb_strlen($candidate) - 3)));
    };

    $formatDurationLabel = static function (array $durations): string {
        if ($durations === []) {
            return 'Belum ada data';
        }

        sort($durations);

        $min = (int) $durations[0];
        $max = (int) $durations[(int) floor((count($durations) - 1) * 0.75)];

        $format = static function (int $minutes): string {
            if ($minutes >= 60) {
                $hours = (int) ceil($minutes / 60);

                return $hours.' jam';
            }

            return $minutes.' menit';
        };

        if ($min === $max) {
            return $format($min);
        }

        return $format($min).'-'.$format($max);
    };

    $ratedTransactions = (clone $completedTransactionsQuery)
        ->whereNotNull('rating_score')
        ->orderByDesc('rated_at')
        ->orderByDesc('updated_at')
        ->limit(9)
        ->get([
            'public_id',
            'customer_name',
            'customer_email',
            'customer_whatsapp',
            'rating_score',
            'rating_comment',
            'rated_at',
            'updated_at',
        ]);

    $totalReviews = (int) ($ratingAggregate?->total_reviews ?? 0);
    $averageScore = $totalReviews > 0 ? (float) ($ratingAggregate?->average_score ?? 0) : 0;
    $recommendationRate = $totalReviews > 0 ? (int) round(((int) ($ratingAggregate?->recommended_reviews ?? 0) / $totalReviews) * 100) : 0;
    $fiveStarRate = $totalReviews > 0 ? (int) round(((int) ($ratingAggregate?->five_star_reviews ?? 0) / $totalReviews) * 100) : 0;
    $ratingTitle = $productFamily['title'] ?: ($resolvedProductName !== '' ? $resolvedProductName : Str::headline(str_replace('-', ' ', $product)));

    $productRatings = [
        'summary' => [
            'average' => number_format($averageScore, 2, '.', ''),
            'totalReviews' => $totalReviews,
            'recommendationRate' => $recommendationRate,
            'fiveStarRate' => $fiveStarRate,
            'processingSpeed' => $formatDurationLabel($processingDurations),
            'familyTitle' => $ratingTitle,
        ],
        'reviews' => $ratedTransactions->map(static function (Transaction $transaction) use ($formatBuyerName): array {
            $comment = trim((string) $transaction->rating_comment);

            return [
                'id' => $transaction->public_id,
                'name' => $formatBuyerName($transaction->customer_name, $transaction->customer_email, $transaction->customer_whatsapp),
                'badge' => 'Verified Buyer',
                'timeLabel' => ($transaction->rated_at ?? $transaction->updated_at)?->locale('id')->translatedFormat('d M Y, H:i') ?? '-',
                'rating' => (int) ($transaction->rating_score ?? 0),
                'comment' => $comment !== '' ? $comment : 'Pesanan diproses lancar, produk masuk sesuai pilihan, dan checkout terasa mudah dipakai.',
            ];
        })->all(),
    ];

    return Inertia::render('ProductDetail', [
        'productId' => $product,
        'vipCatalogProduct' => $vipCatalogProduct,
        'initialVipaymentTabs' => $initialVipaymentTabs,
        'initialVipaymentSource' => $initialVipaymentSource,
        'initialVipaymentMessage' => $initialVipaymentMessage,
        'initialPaymentMethods' => $initialPaymentMethods,
        'initialPaymentMethodsAmount' => $initialPaymentMethodsAmount,
        'productRatings' => $productRatings,
    ]);
})->name('products.show');

Route::get('kalkulator', function () {
    return Inertia::render('Calculators');
})->name('calculators.index');

Route::redirect('cek-transaksi', 'riwayat-transaksi');

Route::get('konfirmasi-pesanan/{transaction}/telegram', [TransactionMonitorController::class, 'confirmFromTelegram'])
    ->middleware('signed')
    ->name('admin.transactions.manual.confirm.telegram');

Route::middleware(['auth', 'admin.panel'])->group(function () {
    Route::get('dashboard', DashboardController::class)->name('dashboard');
    Route::get('dashboard/produk', [ProductSettingsController::class, 'index'])->name('admin.products.index');
    Route::get('dashboard/provider-kosong', ProviderAvailabilityController::class)->name('admin.provider-availability.index');
    Route::get('dashboard/security', [SecurityMonitorController::class, 'index'])->name('admin.security.index');
    Route::get('dashboard/affiliate', [AffiliateMonitorController::class, 'index'])->name('admin.affiliates.index');
    Route::get('dashboard/security/download', [SecurityMonitorController::class, 'download'])->name('admin.security.download');
    Route::post('dashboard/produk/{productId}', [ProductSettingsController::class, 'update'])->name('admin.products.update');
    Route::patch('dashboard/produk/{productId}/visibility', [ProductSettingsController::class, 'updateVisibility'])->name('admin.products.visibility');
    Route::delete('dashboard/produk/{productId}', [ProductSettingsController::class, 'destroy'])->name('admin.products.destroy');
    Route::get('dashboard/promo', [PromoCodeController::class, 'index'])->name('admin.promos.index');
    Route::post('dashboard/promo', [PromoCodeController::class, 'store'])->name('admin.promos.store');
    Route::patch('dashboard/promo/{promo}', [PromoCodeController::class, 'update'])->name('admin.promos.update');
    Route::delete('dashboard/promo/{promo}', [PromoCodeController::class, 'destroy'])->name('admin.promos.destroy');
    Route::get('dashboard/stok-manual', [ManualStockController::class, 'index'])->name('admin.manual-stock.index');
    Route::post('dashboard/stok-manual', [ManualStockController::class, 'store'])->name('admin.manual-stock.store');
    Route::get('dashboard/margin', [MarginSettingsController::class, 'index'])->name('admin.margins.index');
    Route::put('dashboard/margin', [MarginSettingsController::class, 'update'])->name('admin.margins.update');
    Route::get('dashboard/branding', [BrandingSettingsController::class, 'index'])->name('admin.branding.index');
    Route::patch('dashboard/branding', [BrandingSettingsController::class, 'update'])->name('admin.branding.update');
    Route::get('dashboard/transaksi', [TransactionMonitorController::class, 'index'])->name('admin.transactions.index');
    Route::get('dashboard/transaksi/invite-workspace', [TransactionMonitorController::class, 'index'])
        ->defaults('filter', 'invite-queue')
        ->name('admin.transactions.invite-queue');
    Route::post('dashboard/transaksi/{transaction}/manual-complete', [TransactionMonitorController::class, 'complete'])->name('admin.transactions.manual.complete');
    Route::post('dashboard/affiliate/{user}/approve', [AffiliateMonitorController::class, 'approve'])->name('admin.affiliates.approve');
    Route::post('dashboard/affiliate/{user}/reject', [AffiliateMonitorController::class, 'reject'])->name('admin.affiliates.reject');
    Route::post('dashboard/affiliate/withdrawals/{withdrawal}/process', [AffiliateMonitorController::class, 'processWithdrawal'])->name('admin.affiliates.withdrawals.process');
    Route::post('dashboard/affiliate/withdrawals/{withdrawal}/pay', [AffiliateMonitorController::class, 'payWithdrawal'])->name('admin.affiliates.withdrawals.pay');
    Route::post('dashboard/affiliate/withdrawals/{withdrawal}/reject', [AffiliateMonitorController::class, 'rejectWithdrawal'])->name('admin.affiliates.withdrawals.reject');
});

require __DIR__.'/settings.php';
require __DIR__.'/auth.php';
