<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Transaction;
use App\Models\FinanceGroup;
use App\Models\BankAccount;
use App\Models\CryptoAccount;
use App\Models\BlacklistEntry;
use App\Support\SiteCredentialResolver;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Illuminate\Database\QueryException;
use Illuminate\Validation\ValidationException;

class PaymentController extends Controller
{
    public function showDepositForm(Request $request)
    {
        return $this->renderDepositView($request, 'payment.deposit', true);
    }

    public function showDepositFormPreview(Request $request)
    {
        return $this->renderDepositView($request, 'payment.deposit-preview', false);
    }

    private function renderDepositView(Request $request, string $view, bool $enforceTrustedEmbed)
    {
        if ((string) setting('payment_deposit_enabled', 'enabled') !== 'enabled') {
            return view('payment.maintenance');
        }

        $site = SiteCredentialResolver::resolveByCredentials($request->query('sid'), null);
        if (!$site || !$site->is_active) {
            return view('payment.maintenance');
        }

        if ($enforceTrustedEmbed && !$this->isAllowedEmbeddedDepositRequest($request, (string) $site->redirect_url)) {
            $this->logPublicDepositBlock($request, 'untrusted_embed_request');
            return response()->view('payment.blocked', [
                'site_name' => (string) $site->name,
            ], 403);
        }

        $site->load('paymentPopup');
        $popup = $site->paymentPopup;

        $viewData = [
            'player_id' => $request->query('player_id'),
            'player_name' => $request->query('player_name'),
            'site_name' => (string) $site->name,
            'site_logo_url' => $site->logo_url,
            'redirect_url' => (string) $site->redirect_url,
            'site_sid' => (string) $site->sid,
            'deposit_guard_token' => $this->issuePublicDepositGuardToken($request, (string) $site->sid),
            'payment_popup' => [
                'desktop_image_url' => $popup?->desktop_image_url,
                'mobile_image_url' => $popup?->mobile_image_url,
                'desktop_enabled' => (bool) ($popup?->desktop_enabled ?? false),
                'mobile_enabled' => (bool) ($popup?->mobile_enabled ?? false),
                'popup_click_url' => (string) ($popup?->popup_click_url ?? ''),
                'popup_footer_text' => (string) ($popup?->popup_footer_text ?? ''),
                'popup_footer_url' => (string) ($popup?->popup_footer_url ?? ''),
            ],
        ];

        $response = response()->view($view, $viewData);
        $frameAncestorsPolicy = $enforceTrustedEmbed
            ? $this->buildDepositFrameAncestorsPolicy((string) $site->redirect_url)
            : null;
        if ($frameAncestorsPolicy !== null) {
            $response->headers->set('Content-Security-Policy', $frameAncestorsPolicy);
        }

        return $response;
    }

    private function buildAllowedEmbedOrigins(string $redirectUrl): array
    {
        $parts = parse_url(trim($redirectUrl));
        $scheme = strtolower((string) ($parts['scheme'] ?? ''));
        $host = strtolower((string) ($parts['host'] ?? ''));
        $port = isset($parts['port']) ? (int) $parts['port'] : null;

        if ($scheme === '' || $host === '') {
            return [];
        }

        $hosts = [$host];
        if (str_starts_with($host, 'www.')) {
            $hosts[] = substr($host, 4);
        } elseif (substr_count($host, '.') === 1) {
            $hosts[] = 'www.' . $host;
        }

        $origins = [];
        foreach (array_unique($hosts) as $candidateHost) {
            $origin = $scheme . '://' . $candidateHost;
            if ($port !== null && !in_array($port, [80, 443], true)) {
                $origin .= ':' . $port;
            }
            $origins[] = $origin;
        }

        return array_values(array_unique($origins));
    }

    private function buildDepositFrameAncestorsPolicy(string $redirectUrl): ?string
    {
        $allowedOrigins = $this->buildAllowedEmbedOrigins($redirectUrl);
        if ($allowedOrigins === []) {
            return null;
        }

        return "frame-ancestors 'self' " . implode(' ', $allowedOrigins) . ';';
    }

    private function isAllowedEmbeddedDepositRequest(Request $request, string $redirectUrl): bool
    {
        $allowedOrigins = $this->buildAllowedEmbedOrigins($redirectUrl);
        if ($allowedOrigins === []) {
            return false;
        }

        $secFetchDest = strtolower(trim((string) $request->headers->get('Sec-Fetch-Dest', '')));
        if ($secFetchDest !== '' && $secFetchDest !== 'iframe') {
            return false;
        }

        $referer = trim((string) $request->headers->get('referer', ''));
        if ($referer === '') {
            return true;
        }

        $refererParts = parse_url($referer);
        $refererScheme = strtolower((string) ($refererParts['scheme'] ?? ''));
        $refererHost = strtolower((string) ($refererParts['host'] ?? ''));
        $refererPort = isset($refererParts['port']) ? (int) $refererParts['port'] : null;
        if ($refererScheme === '' || $refererHost === '') {
            return true;
        }

        $refererOrigin = $refererScheme . '://' . $refererHost;
        if ($refererPort !== null && !in_array($refererPort, [80, 443], true)) {
            $refererOrigin .= ':' . $refererPort;
        }

        return in_array($refererOrigin, $allowedOrigins, true);
    }

    private function issuePublicDepositGuardToken(Request $request, string $sid, ?array $payload = null): string
    {
        $now = now()->timestamp;

        $token = Str::random(64);
        $guardPayload = $payload ?: [
            'sid' => $sid,
            'player_id' => trim((string) $request->query('player_id', '')),
            'player_name' => trim((string) $request->query('player_name', '')),
            'requested_trx' => trim((string) $request->query('trx', '')),
            'initialized_trx' => null,
            'initialized_at' => null,
            'stage' => 'initialize',
            'issued_at' => $now,
        ];

        Cache::put($this->publicDepositGuardPayloadCacheKey($token), $guardPayload, now()->addHour());
        $this->registerPublicDepositGuardToken($token);

        return $token;
    }

    private function ensureInitializeIpBurstAllowed(Request $request)
    {
        $ipAddress = trim((string) $request->ip());
        if ($ipAddress === '') {
            return null;
        }

        $lockKey = 'public_deposit_initialize_lock:' . $ipAddress;
        if (Cache::has($lockKey)) {
            $this->logPublicDepositBlock($request, 'initialize_ip_locked');

            return response()->json([
                'success' => false,
                'message' => 'Cok sik istek algilandi. Lutfen daha sonra tekrar deneyin.',
            ], 429);
        }

        $burstKey = 'public_deposit_initialize_burst:' . $ipAddress;
        $burstCount = ((int) Cache::get($burstKey, 0)) + 1;
        Cache::put($burstKey, $burstCount, now()->addSeconds(10));

        if ($burstCount < 4) {
            return null;
        }

        Cache::put($lockKey, 1, now()->addMinutes(15));
        Cache::forget($burstKey);
        $this->logPublicDepositBlock($request, 'initialize_ip_burst_detected');

        return response()->json([
            'success' => false,
            'message' => 'Cok sik istek algilandi. Bu IP gecici olarak engellendi.',
        ], 429);
    }

    private function validatePublicDepositGuard(Request $request, bool $requireInitializedTrx = false): string
    {
        $honeypot = trim((string) $request->input('company_website', ''));
        if ($honeypot !== '') {
            $this->logPublicDepositBlock($request, 'honeypot_filled');
            throw ValidationException::withMessages([
                'company_website' => 'Form dogrulamasi basarisiz.',
            ]);
        }

        $token = trim((string) $request->input('deposit_guard_token', ''));
        $guard = Cache::get($this->publicDepositGuardPayloadCacheKey($token));

        if ($token === '' || !is_array($guard)) {
            $this->logPublicDepositBlock($request, 'missing_or_invalid_guard_token');
            throw ValidationException::withMessages([
                'deposit_guard_token' => 'Oturum dogrulamasi gecersiz. Sayfayi yenileyip tekrar deneyin.',
            ]);
        }

        if (!$this->isPublicDepositGuardTokenRegistered($token)) {
            $this->logPublicDepositBlock($request, 'inactive_or_consumed_guard_token');
            throw ValidationException::withMessages([
                'deposit_guard_token' => 'Bu odeme istegi daha once kullanildi. Sayfayi yenileyip tekrar deneyin.',
            ]);
        }

        $issuedAt = (int) ($guard['issued_at'] ?? 0);
        if ($issuedAt <= 0 || (now()->timestamp - $issuedAt) > 3600) {
            $this->forgetPublicDepositGuardToken($token);
            $this->logPublicDepositBlock($request, 'expired_guard_token');

            throw ValidationException::withMessages([
                'deposit_guard_token' => 'Oturum suresi doldu. Sayfayı yenileyip tekrar deneyin.',
            ]);
        }

        if ((now()->timestamp - $issuedAt) < 2) {
            $this->logPublicDepositBlock($request, 'guard_token_used_too_early');
            throw ValidationException::withMessages([
                'deposit_guard_token' => 'Form dogrulaması için lütfen birkaç saniye bekleyip tekrar deneyin.',
            ]);
        }

        $expectedStage = $requireInitializedTrx ? 'confirm' : 'initialize';
        if ((string) ($guard['stage'] ?? '') !== $expectedStage) {
            $this->logPublicDepositBlock($request, 'guard_token_stage_mismatch');
            throw ValidationException::withMessages([
                'deposit_guard_token' => 'Lütfen Sayfayı Kapatıp Tekrar Deneyiniz.',
            ]);
        }

        if (!hash_equals((string) ($guard['sid'] ?? ''), trim((string) $request->input('sid', '')))) {
            $this->logPublicDepositBlock($request, 'sid_mismatch');
            throw ValidationException::withMessages([
                'deposit_guard_token' => 'Site dogrulamasi basarisiz.',
            ]);
        }

        if (!hash_equals((string) ($guard['player_id'] ?? ''), trim((string) $request->input('customer_player_id', '')))) {
            $this->logPublicDepositBlock($request, 'player_id_mismatch');
            throw ValidationException::withMessages([
                'customer_player_id' => 'Uye dogrulamasi basarisiz. Sayfayi yenileyip tekrar deneyin.',
            ]);
        }

        if (!hash_equals((string) ($guard['player_name'] ?? ''), trim((string) $request->input('customer_name', '')))) {
            $this->logPublicDepositBlock($request, 'player_name_mismatch');
            throw ValidationException::withMessages([
                'customer_name' => 'Kullanici dogrulamasi basarisiz. Sayfayi yenileyip tekrar deneyin.',
            ]);
        }

        if ($requireInitializedTrx) {
            $initializedAt = (int) ($guard['initialized_at'] ?? 0);
            if ($initializedAt <= 0 || (now()->timestamp - $initializedAt) < 2) {
                $this->logPublicDepositBlock($request, 'confirm_used_too_early');
                throw ValidationException::withMessages([
                    'trx' => 'Odeme onayi icin lutfen kisa bir sure bekleyip tekrar deneyin.',
                ]);
            }

            if (!hash_equals((string) ($guard['initialized_trx'] ?? ''), trim((string) $request->input('trx', '')))) {
                $this->logPublicDepositBlock($request, 'initialized_trx_mismatch');
                throw ValidationException::withMessages([
                    'trx' => 'Islem dogrulamasi basarisiz. Lutfen odeme bilgisini yeniden isteyin.',
                ]);
            }
        } elseif (!hash_equals((string) ($guard['requested_trx'] ?? ''), trim((string) $request->input('trx', '')))) {
            $this->logPublicDepositBlock($request, 'requested_trx_mismatch');
            throw ValidationException::withMessages([
                'trx' => 'Form oturumu ile islem bilgisi eslesmiyor. Sayfayi yenileyip tekrar deneyin.',
            ]);
        }

        return $token;
    }

    private function prepareConfirmDepositGuardToken(Request $request, string $token, string $trx): string
    {
        $guard = Cache::get($this->publicDepositGuardPayloadCacheKey($token));
        if (!is_array($guard)) {
            throw ValidationException::withMessages([
                'deposit_guard_token' => 'Form oturumu gecersiz. Sayfayi yenileyin.',
            ]);
        }

        $this->consumeOrFailPublicDepositGuardToken($request, $token, 'initialize_guard_token_reused');

        $guard['initialized_trx'] = $trx;
        $guard['initialized_at'] = now()->timestamp;
        $guard['stage'] = 'confirm';
        $guard['issued_at'] = now()->timestamp;

        return $this->issuePublicDepositGuardToken($request, (string) ($guard['sid'] ?? ''), $guard);
    }

    private function forgetPublicDepositGuard(Request $request, string $token): void
    {
        $this->forgetPublicDepositGuardToken($token);
    }

    private function registerPublicDepositGuardToken(string $token): void
    {
        Cache::put($this->publicDepositGuardActiveCacheKey($token), 1, now()->addHour());
    }

    private function isPublicDepositGuardTokenRegistered(string $token): bool
    {
        return Cache::has($this->publicDepositGuardActiveCacheKey($token));
    }

    private function consumeOrFailPublicDepositGuardToken(Request $request, string $token, string $reason): void
    {
        $consumed = Cache::add($this->publicDepositGuardConsumedCacheKey($token), 1, now()->addHour());
        if (!$consumed) {
            $this->logPublicDepositBlock($request, $reason);
            throw ValidationException::withMessages([
                'deposit_guard_token' => 'Bu odeme istegi daha once kullanildi. Sayfayi yenileyip tekrar deneyin.',
            ]);
        }

        $this->forgetPublicDepositGuardToken($token);
    }

    private function forgetPublicDepositGuardToken(string $token): void
    {
        Cache::forget($this->publicDepositGuardPayloadCacheKey($token));
        Cache::forget($this->publicDepositGuardActiveCacheKey($token));
    }

    private function publicDepositGuardPayloadCacheKey(string $token): string
    {
        return 'public_deposit_guard:payload:' . $token;
    }

    private function publicDepositGuardActiveCacheKey(string $token): string
    {
        return 'public_deposit_guard:active:' . $token;
    }

    private function publicDepositGuardConsumedCacheKey(string $token): string
    {
        return 'public_deposit_guard:consumed:' . $token;
    }

    private function claimPublicDepositTransactionGuard(Request $request, int $siteId, string $merchantTrxId): void
    {
        try {
            DB::table('public_deposit_request_guards')->insert([
                'site_id' => $siteId,
                'merchant_trx_id' => $merchantTrxId,
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        } catch (QueryException $e) {
            if ($this->isDuplicateKeyException($e)) {
                $this->logPublicDepositBlock($request, 'duplicate_public_deposit_trx');
                throw ValidationException::withMessages([
                    'trx' => 'Bu odeme istegi daha once kullanildi. Sayfayi yenileyip tekrar deneyin.',
                ]);
            }

            throw $e;
        }
    }

    private function isDuplicateKeyException(QueryException $e): bool
    {
        $sqlState = (string) ($e->errorInfo[0] ?? '');
        $driverCode = (string) ($e->errorInfo[1] ?? '');

        return $sqlState === '23000' || $driverCode === '1062';
    }

    private function isPublicDepositRequestBlacklisted(?string $playerId = null): bool
    {
        $playerId = trim((string) $playerId);
        if ($playerId === '') {
            return false;
        }

        return BlacklistEntry::where('player_id', $playerId)->exists();
    }

    private function logPublicDepositBlock(Request $request, string $reason): void
    {
        Log::warning('Public deposit request blocked', [
            'reason' => $reason,
            'ip' => $request->ip(),
            'sid' => (string) $request->input('sid', $request->query('sid', '')),
            'player_id' => (string) $request->input('customer_player_id', $request->query('player_id', '')),
            'trx' => (string) $request->input('trx', $request->query('trx', '')),
            'user_agent' => (string) $request->userAgent(),
            'url' => (string) $request->fullUrl(),
        ]);
    }

    private function validationErrorResponse(ValidationException $e)
    {
        $errors = $e->errors();
        $firstMessage = 'Dogrulama hatasi.';

        foreach ($errors as $messages) {
            if (!empty($messages[0])) {
                $firstMessage = (string) $messages[0];
                break;
            }
        }

        return response()->json([
            'success' => false,
            'message' => $firstMessage,
            'errors' => $errors,
        ], 422);
    }

    public function initializeDepositPreview(Request $request)
    {
        if ((string) setting('payment_deposit_enabled', 'enabled') !== 'enabled') {
            return response()->json([
                'success' => false,
                'message' => 'Sistem bakimdadir. Lutfen daha sonra tekrar deneyiniz veya canli destek ile iletisime geciniz.',
            ], 503);
        }

        if ($burstBlockedResponse = $this->ensureInitializeIpBurstAllowed($request)) {
            return $burstBlockedResponse;
        }

        $request->validate([
            'amount' => 'required|numeric|min:1',
            'customer_name' => 'required|string|max:255|not_regex:/[0-9]/',
            'customer_player_id' => 'required|string|max:255',
            'sid' => 'required|string|max:255',
            'trx' => 'nullable|string|max:255',
            'payment_method' => 'required|in:bank,crypto',
            'deposit_guard_token' => 'required|string|size:64',
            'company_website' => 'nullable|string|max:255',
        ], [
            'amount.required' => 'Tutar gereklidir.',
            'amount.min' => 'Tutar 0dan buyuk olmalidir.',
            'customer_name.required' => 'Adiniz gereklidir.',
            'customer_name.not_regex' => 'Error accord 315.',
            'customer_player_id.required' => 'Uye ID gereklidir.',
            'sid.required' => 'Site key gereklidir.',
            'payment_method.required' => 'Odeme yontemi secilmelidir.',
            'payment_method.in' => 'Gecersiz odeme yontemi.',
            'deposit_guard_token.required' => 'Form oturumu gecersiz. Sayfayi yenileyin.',
        ]);

        try {
            $guardToken = $this->validatePublicDepositGuard($request, false);
            $site = SiteCredentialResolver::resolveByCredentials($request->sid, null);
            if (!$site) {
                return response()->json([
                    'success' => false,
                    'message' => 'Gecersiz site key.',
                ], 403);
            }
            if (!$site->is_active) {
                return response()->json([
                    'success' => false,
                    'message' => 'Site pasif durumda.',
                ], 403);
            }

            if ($this->isPublicDepositRequestBlacklisted((string) $request->customer_player_id)) {
                $this->logPublicDepositBlock($request, 'blacklisted_player');
                return response()->json([
                    'success' => false,
                    'message' => 'Player is blocked.',
                ], 403);
            }

            $hasPending = Transaction::where('type', 'deposit')
                ->where('status', 'pending')
                ->where('customer_player_id', $request->customer_player_id)
                ->exists();
            if ($hasPending) {
                return response()->json([
                    'success' => false,
                    'message' => 'Bekleyen Yatirim Talebiniz Var, Lutfen Bekleyiniz.',
                ], 409);
            }

            $amount = (float) $request->amount;
            $paymentMethod = (string) $request->payment_method;
            $financeGroup = null;

            if ($paymentMethod === 'bank') {
                $financeGroup = $this->resolvePreviewFinanceGroup($amount, (int) $site->id, 'bank');
                if (!$financeGroup) {
                    return response()->json([
                        'success' => false,
                        'message' => 'Bu tutar icin uygun finans grubu bulunamadi.',
                    ], 400);
                }
            } else {
                if ($amount < 1000) {
                    return response()->json([
                        'success' => false,
                        'message' => 'Kripto odeme icin minimum tutar 1000 olmalidir.',
                    ], 400);
                }
            }

            $trx = $request->trx ?: ('TRX-' . uniqid());
            $nextGuardToken = $this->prepareConfirmDepositGuardToken($request, $guardToken, $trx);
            $response = [
                'success' => true,
                'message' => $paymentMethod === 'crypto'
                    ? 'Kripto hesaplari alindi.'
                    : 'Banka hesabi bilgileri alindi.',
                'deposit_guard_token' => $nextGuardToken,
                'preview' => [
                    'payment_method' => $paymentMethod,
                    'amount' => $amount,
                    'amount_formatted' => number_format($amount, 2, ',', '.'),
                    'finance_group_id' => $financeGroup ? (int) $financeGroup->id : null,
                    'customer_name' => $request->customer_name,
                    'customer_player_id' => $request->customer_player_id,
                    'sid' => $site->sid,
                    'site_id' => $site->id,
                    'trx' => $trx,
                    'deposit_guard_token' => $nextGuardToken,
                ],
            ];

            if ($paymentMethod === 'bank') {
                $bankAccounts = BankAccount::where('finance_group_id', $financeGroup->id)
                    ->with(['siteStatuses' => function ($query) use ($site) {
                        $query->where('site_id', $site->id);
                    }])
                    ->get()
                    ->filter(function (BankAccount $account) use ($site) {
                        return $account->isActiveForSite((int) $site->id);
                    })
                    ->values();

                $bankAccount = $bankAccounts->isNotEmpty() ? $bankAccounts->random() : null;
                if (!$bankAccount) {
                    return response()->json([
                        'success' => false,
                        'message' => 'Bu tutar icin uygun banka hesabi bulunamadi.',
                    ], 400);
                }

                $response['preview']['bank_account_id'] = (int) $bankAccount->id;
                $response['bank_account'] = [
                    'account_name' => $bankAccount->account_name,
                    'bank_name' => $bankAccount->bank_name,
                    'iban' => $bankAccount->iban,
                    'description' => $bankAccount->description,
                ];

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

            $cryptoAccounts = CryptoAccount::query()
                ->where('is_active', true)
                ->whereNotNull('address')
                ->where('address', '!=', '')
                ->with('coin:id,name,icon')
                ->orderBy('id')
                ->get();

            if ($cryptoAccounts->isEmpty()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Aktif kripto hesabi bulunamadi.',
                ], 400);
            }

            $response['preview']['crypto_account_id'] = (int) $cryptoAccounts->first()->id;
            $response['crypto_accounts'] = $cryptoAccounts->map(function (CryptoAccount $account) {
                return [
                    'id' => (int) $account->id,
                    'coin_name' => (string) optional($account->coin)->name,
                    'coin_icon' => $this->resolveCoinIconUrl(optional($account->coin)->icon),
                    'address' => (string) $account->address,
                ];
            })->values();

            return response()->json($response);
        } catch (ValidationException $e) {
            return $this->validationErrorResponse($e);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Islem olusturulurken bir hata olustu: ' . $e->getMessage(),
            ], 500);
        }
    }

    public function confirmDepositPreview(Request $request)
    {
        if ((string) setting('payment_deposit_enabled', 'enabled') !== 'enabled') {
            return response()->json([
                'success' => false,
                'message' => 'Sistem bakimdadir. Lutfen daha sonra tekrar deneyiniz veya canli destek ile iletisime geciniz.',
            ], 503);
        }

        $request->validate([
            'amount' => 'required|numeric|min:1',
            'customer_name' => 'required|string|max:255|not_regex:/[0-9]/',
            'customer_player_id' => 'required|string|max:255',
            'finance_group_id' => 'nullable|exists:finance_groups,id',
            'sid' => 'required|string|max:255',
            'trx' => 'nullable|string|max:255',
            'payment_method' => 'required|in:bank,crypto',
            'bank_account_id' => 'nullable|exists:bank_accounts,id',
            'crypto_account_id' => 'nullable|exists:crypto_accounts,id',
            'deposit_guard_token' => 'required|string|size:64',
        ], [
            'amount.required' => 'Tutar gereklidir.',
            'customer_name.required' => 'Adiniz gereklidir.',
            'customer_name.not_regex' => 'Error accord 315.',
            'customer_player_id.required' => 'Uye ID gereklidir.',
            'sid.required' => 'Site key gereklidir.',
            'payment_method.required' => 'Odeme yontemi secilmelidir.',
            'payment_method.in' => 'Gecersiz odeme yontemi.',
            'deposit_guard_token.required' => 'Form oturumu gecersiz. Sayfayi yenileyin.',
        ]);

        DB::beginTransaction();
        try {
            $guardToken = $this->validatePublicDepositGuard($request, true);
            $site = SiteCredentialResolver::resolveByCredentials($request->sid, null);
            if (!$site) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Gecersiz site key.',
                ], 403);
            }
            if (!$site->is_active) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Site pasif durumda.',
                ], 403);
            }

            if ($this->isPublicDepositRequestBlacklisted((string) $request->customer_player_id)) {
                $this->logPublicDepositBlock($request, 'blacklisted_player');
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Player is blocked.',
                ], 403);
            }

            $hasPending = Transaction::where('type', 'deposit')
                ->where('status', 'pending')
                ->where('customer_player_id', $request->customer_player_id)
                ->exists();
            if ($hasPending) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Bekleyen Yatirim Talebiniz Var, Lutfen Bekleyiniz.',
                ], 409);
            }

            $amount = (float) $request->amount;
            $paymentMethod = (string) $request->payment_method;
            $financeGroup = null;
            $financeGroupId = null;

            $bankAccountId = null;
            $description = null;
            $cryptoAccountId = null;
            $cryptoCoinName = null;
            $cryptoAddress = null;

            if ($paymentMethod === 'bank') {
                $financeGroup = $this->resolvePreviewFinanceGroup($amount, (int) $site->id, 'bank');
                if (!$financeGroup) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => 'Bu tutar icin uygun finans grubu bulunamadi.',
                    ], 400);
                }

                if ((int) $request->finance_group_id !== (int) $financeGroup->id) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => 'Secilen finans grubu gecersiz.',
                    ], 400);
                }

                if (!$request->filled('bank_account_id')) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => 'Banka hesabi secilmelidir.',
                    ], 422);
                }

                $bankAccount = BankAccount::where('id', $request->bank_account_id)
                    ->where('finance_group_id', $financeGroup->id)
                    ->with(['siteStatuses' => function ($query) use ($site) {
                        $query->where('site_id', $site->id);
                    }])
                    ->first();

                if (!$bankAccount || !$bankAccount->isActiveForSite((int) $site->id)) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => 'Secilen banka hesabi gecersiz.',
                    ], 400);
                }

                $bankAccountId = (int) $bankAccount->id;
                $financeGroupId = (int) $financeGroup->id;
            } else {
                if ($amount < 1000) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => 'Kripto odeme icin minimum tutar 1000 olmalidir.',
                    ], 400);
                }

                if (!$request->filled('crypto_account_id')) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => 'Kripto hesabi secilmelidir.',
                    ], 422);
                }

                $cryptoAccount = CryptoAccount::query()
                    ->where('id', $request->crypto_account_id)
                    ->where('is_active', true)
                    ->whereNotNull('address')
                    ->where('address', '!=', '')
                    ->with('coin:id,name')
                    ->first();

                if (!$cryptoAccount) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => 'Secilen kripto hesabi gecersiz.',
                    ], 400);
                }

                $financeGroupId = (int) $cryptoAccount->finance_group_id;
                if ($financeGroupId <= 0) {
                    DB::rollBack();
                    return response()->json([
                        'success' => false,
                        'message' => 'Secilen kripto hesabi icin finans grubu bulunamadi.',
                    ], 400);
                }

                $cryptoAccountId = (int) $cryptoAccount->id;
                $cryptoCoinName = (string) optional($cryptoAccount->coin)->name;
                $cryptoAddress = (string) $cryptoAccount->address;
                $description = trim('KRIPTO ' . $cryptoCoinName . ' - ' . $cryptoAddress);
            }

            $transactionId = Transaction::generateUniqueTransactionId('D');
            $trx = $request->trx ?: $transactionId;

            $transactionPayload = [
                'transaction_id' => $transactionId,
                'type' => 'deposit',
                'source' => 'auto',
                'status' => 'pending',
                'amount' => $amount,
                'currency' => 'TRY',
                'customer_name' => $request->customer_name,
                'customer_player_id' => $request->customer_player_id,
                'merchant_trx_id' => $trx,
                'site_id' => $site->id,
                'sid' => $site->sid,
                'api_key' => $site->api_key,
                'finance_group_id' => $financeGroupId,
                'bank_account_id' => $bankAccountId,
                'ip_address' => $request->ip(),
                'description' => $description,
            ];

            $transactionPayload = $this->withTransactionPaymentPayload(
                $transactionPayload,
                $paymentMethod,
                $cryptoAccountId,
                $cryptoCoinName,
                $cryptoAddress
            );

            $this->consumeOrFailPublicDepositGuardToken($request, $guardToken, 'confirm_guard_token_reused');
            $this->claimPublicDepositTransactionGuard($request, (int) $site->id, (string) $trx);
            $transaction = Transaction::create($transactionPayload);

            DB::commit();
            $this->forgetPublicDepositGuard($request, $guardToken);

            return response()->json([
                'success' => true,
                'message' => 'Islem basariyla olusturuldu.',
                'transaction' => [
                    'id' => $transaction->transaction_id,
                    'amount' => number_format((float) $transaction->amount, 2, ',', '.'),
                ],
            ]);
        } catch (ValidationException $e) {
            DB::rollBack();
            return $this->validationErrorResponse($e);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Islem olusturulurken bir hata olustu: ' . $e->getMessage(),
            ], 500);
        }
    }

    public function initializeDeposit(Request $request)
    {
        if ((string) setting('payment_deposit_enabled', 'enabled') !== 'enabled') {
            return response()->json([
                'success' => false,
                'message' => 'Sistem bakimdadir. Lutfen daha sonra tekrar deneyiniz veya canli destek ile iletisime geciniz.',
            ], 503);
        }

        if ($burstBlockedResponse = $this->ensureInitializeIpBurstAllowed($request)) {
            return $burstBlockedResponse;
        }

        $request->validate([
            'amount' => 'required|numeric|min:1',
            'customer_name' => 'required|string|max:255|not_regex:/[0-9]/',
            'customer_player_id' => 'required|string|max:255',
            'sid' => 'required|string|max:255',
            'trx' => 'nullable|string|max:255',
            'deposit_guard_token' => 'required|string|size:64',
            'company_website' => 'nullable|string|max:255',
        ], [
            'amount.required' => 'Tutar gereklidir.',
            'amount.min' => 'Tutar 0dan buyuk olmalidir.',
            'customer_name.required' => 'Adiniz gereklidir.',
            'customer_name.not_regex' => 'Error accord 315.',
            'customer_player_id.required' => 'Uye ID gereklidir.',
            'sid.required' => 'Site key gereklidir.',
            'deposit_guard_token.required' => 'Form oturumu gecersiz. Sayfayi yenileyin.',
        ]);

        try {
            $guardToken = $this->validatePublicDepositGuard($request, false);
            $site = SiteCredentialResolver::resolveByCredentials($request->sid, null);
            if (!$site) {
                return response()->json([
                    'success' => false,
                    'message' => 'Gecersiz site key.',
                ], 403);
            }
            if (!$site->is_active) {
                return response()->json([
                    'success' => false,
                    'message' => 'Site pasif durumda.',
                ], 403);
            }

            if ($this->isPublicDepositRequestBlacklisted((string) $request->customer_player_id)) {
                $this->logPublicDepositBlock($request, 'blacklisted_player');
                return response()->json([
                    'success' => false,
                    'message' => 'Player is blocked.',
                ], 403);
            }
            $hasPending = Transaction::where('type', 'deposit')
                ->where('status', 'pending')
                ->where('customer_player_id', $request->customer_player_id)
                ->exists();
            if ($hasPending) {
                return response()->json([
                    'success' => false,
                    'message' => 'Bekleyen Yatirim Talebiniz Var, Lutfen Bekleyiniz.',
                ], 409);
            }

            $amount = $request->amount;

            $financeGroup = FinanceGroup::query()
                ->with(['siteLimits' => function ($query) use ($site) {
                    $query->where('site_id', $site->id);
                }, 'siteStatuses' => function ($query) use ($site) {
                    $query->where('site_id', $site->id);
                }])
                ->orderBy('id')
                ->get()
                ->first(function (FinanceGroup $group) use ($amount, $site) {
                    return $group->isActiveForSite((int) $site->id)
                        && $group->isAmountInRangeForSite($amount, (int) $site->id);
                });

            if (!$financeGroup) {
                return response()->json([
                    'success' => false,
                    'message' => 'Bu tutar için uygun finans grubu bulunamadı.',
                ], 400);
            }

            $bankAccounts = BankAccount::where('finance_group_id', $financeGroup->id)
                ->with(['siteStatuses' => function ($query) use ($site) {
                    $query->where('site_id', $site->id);
                }])
                ->get()
                ->filter(function (BankAccount $account) use ($site) {
                    return $account->isActiveForSite((int) $site->id);
                })
                ->values();

            $bankAccount = $bankAccounts->isNotEmpty() ? $bankAccounts->random() : null;

            if (!$bankAccount) {
                return response()->json([
                    'success' => false,
                    'message' => 'Bu tutar için uygun banka hesabı bulunamadı.',
                ], 400);
            }

            $trx = $request->trx;
            if (!$trx) {
                $trx = 'TRX-' . uniqid();
            }
            $nextGuardToken = $this->prepareConfirmDepositGuardToken($request, $guardToken, $trx);

            return response()->json([
                'success' => true,
                'message' => 'Banka hesabı bilgileri alındı.',
                'deposit_guard_token' => $nextGuardToken,
                'preview' => [
                    'amount' => $amount,
                    'amount_formatted' => number_format($amount, 2, ',', '.'),
                    'finance_group_id' => $financeGroup->id,
                    'bank_account_id' => $bankAccount->id,
                    'customer_name' => $request->customer_name,
                    'customer_player_id' => $request->customer_player_id,
                    'sid' => $site->sid,
                    'site_id' => $site->id,
                    'trx' => $trx,
                    'deposit_guard_token' => $nextGuardToken,
                ],
                'bank_account' => [
                    'account_name' => $bankAccount->account_name,
                    'bank_name' => $bankAccount->bank_name,
                    'iban' => $bankAccount->iban,
                    'description' => $bankAccount->description,
                ],
            ]);
        } catch (ValidationException $e) {
            return $this->validationErrorResponse($e);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'İşlem oluşturulurken bir hata oluştu: ' . $e->getMessage(),
            ], 500);
        }
    }

    public function confirmDeposit(Request $request)
    {
        if ((string) setting('payment_deposit_enabled', 'enabled') !== 'enabled') {
            return response()->json([
                'success' => false,
                'message' => 'Sistem bakimdadir. Lutfen daha sonra tekrar deneyiniz veya canli destek ile iletisime geciniz.',
            ], 503);
        }

        $request->validate([
            'amount' => 'required|numeric|min:1',
            'customer_name' => 'required|string|max:255|not_regex:/[0-9]/',
            'customer_player_id' => 'required|string|max:255',
            'finance_group_id' => 'required|exists:finance_groups,id',
            'bank_account_id' => 'required|exists:bank_accounts,id',
            'sid' => 'required|string|max:255',
            'trx' => 'nullable|string|max:255',
            'deposit_guard_token' => 'required|string|size:64',
        ], [
            'amount.required' => 'Tutar gereklidir.',
            'customer_name.required' => 'Adiniz gereklidir.',
            'customer_name.not_regex' => 'Error accord 315.',
            'customer_player_id.required' => 'Uye ID gereklidir.',
            'finance_group_id.required' => 'Finans grubu gereklidir.',
            'bank_account_id.required' => 'Banka hesabi gereklidir.',
            'sid.required' => 'Site key gereklidir.',
            'deposit_guard_token.required' => 'Form oturumu gecersiz. Sayfayi yenileyin.',
        ]);

        DB::beginTransaction();
        try {
            $guardToken = $this->validatePublicDepositGuard($request, true);
            $site = SiteCredentialResolver::resolveByCredentials($request->sid, null);
            if (!$site) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Gecersiz site key.',
                ], 403);
            }
            if (!$site->is_active) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Site pasif durumda.',
                ], 403);
            }

            if ($this->isPublicDepositRequestBlacklisted((string) $request->customer_player_id)) {
                $this->logPublicDepositBlock($request, 'blacklisted_player');
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Player is blocked.',
                ], 403);
            }
            $hasPending = Transaction::where('type', 'deposit')
                ->where('status', 'pending')
                ->where('customer_player_id', $request->customer_player_id)
                ->exists();
            if ($hasPending) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Bekleyen Yatirim Talebiniz Var, Lutfen Bekleyiniz.',
                ], 409);
            }

            $amount = $request->amount;

            $financeGroup = FinanceGroup::query()
                ->with(['siteLimits' => function ($query) use ($site) {
                    $query->where('site_id', $site->id);
                }, 'siteStatuses' => function ($query) use ($site) {
                    $query->where('site_id', $site->id);
                }])
                ->orderBy('id')
                ->get()
                ->first(function (FinanceGroup $group) use ($amount, $site) {
                    return $group->isActiveForSite((int) $site->id)
                        && $group->isAmountInRangeForSite($amount, (int) $site->id);
                });

            if (!$financeGroup) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Bu tutar için uygun finans grubu bulunamadı.',
                ], 400);
            }

            if ((int) $request->finance_group_id !== (int) $financeGroup->id) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Seçilen finans grubu geçersiz.',
                ], 400);
            }

            $bankAccount = BankAccount::where('id', $request->bank_account_id)
                ->where('finance_group_id', $financeGroup->id)
                ->with(['siteStatuses' => function ($query) use ($site) {
                    $query->where('site_id', $site->id);
                }])
                ->first();

            if (!$bankAccount || !$bankAccount->isActiveForSite((int) $site->id)) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'Seçilen banka hesabı geçersiz.',
                ], 400);
            }

            $transactionId = Transaction::generateUniqueTransactionId('D');
            $trx = $request->trx ?: $transactionId;

            $transactionPayload = [
                'transaction_id' => $transactionId,
                'type' => 'deposit',
                'source' => 'auto',
                'status' => 'pending',
                'amount' => $amount,
                'currency' => 'TRY',
                'customer_name' => $request->customer_name,
                'customer_player_id' => $request->customer_player_id,
                'merchant_trx_id' => $trx,
                'site_id' => $site->id,
                'sid' => $site->sid,
                'api_key' => $site->api_key,
                'finance_group_id' => $financeGroup->id,
                'bank_account_id' => $bankAccount->id,
                'ip_address' => $request->ip(),
                'description' => null,
            ];

            $transactionPayload = $this->withTransactionPaymentPayload(
                $transactionPayload,
                'bank'
            );

            $this->consumeOrFailPublicDepositGuardToken($request, $guardToken, 'confirm_guard_token_reused');
            $this->claimPublicDepositTransactionGuard($request, (int) $site->id, (string) $trx);
            $transaction = Transaction::create($transactionPayload);

            DB::commit();
            $this->forgetPublicDepositGuard($request, $guardToken);

            return response()->json([
                'success' => true,
                'message' => 'İşlem başarıyla oluşturuldu.',
                'transaction' => [
                    'id' => $transaction->transaction_id,
                    'amount' => number_format($transaction->amount, 2, ',', '.'),
                ],
            ]);
        } catch (ValidationException $e) {
            DB::rollBack();
            return $this->validationErrorResponse($e);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'İşlem oluşturulurken bir hata oluştu: ' . $e->getMessage(),
            ], 500);
        }
    }

    private function withTransactionPaymentPayload(
        array $payload,
        string $paymentMethod,
        ?int $cryptoAccountId = null,
        ?string $cryptoCoinName = null,
        ?string $cryptoAddress = null
    ): array {
        if ($this->transactionColumnExists('payment_method')) {
            $payload['payment_method'] = $paymentMethod === 'crypto' ? 'crypto' : 'bank';
        }

        if ($this->transactionColumnExists('is_crypto')) {
            $payload['is_crypto'] = ($paymentMethod === 'crypto');
        }

        if ($this->transactionColumnExists('crypto_account_id')) {
            $payload['crypto_account_id'] = $paymentMethod === 'crypto' ? $cryptoAccountId : null;
        }

        if ($this->transactionColumnExists('crypto_coin_name')) {
            $payload['crypto_coin_name'] = $paymentMethod === 'crypto' ? $cryptoCoinName : null;
        }

        if ($this->transactionColumnExists('crypto_address')) {
            $payload['crypto_address'] = $paymentMethod === 'crypto' ? $cryptoAddress : null;
        }

        return $payload;
    }

    private function transactionColumnExists(string $column): bool
    {
        static $columns = [];
        if (!array_key_exists($column, $columns)) {
            $columns[$column] = Schema::hasColumn('transactions', $column);
        }

        return $columns[$column];
    }

    private function resolvePreviewFinanceGroup(float $amount, int $siteId, string $paymentMethod): ?FinanceGroup
    {
        $query = FinanceGroup::query()
            ->with(['siteLimits' => function ($query) use ($siteId) {
                $query->where('site_id', $siteId);
            }, 'siteStatuses' => function ($query) use ($siteId) {
                $query->where('site_id', $siteId);
            }]);

        return $query->orderBy('id')->get()->first(function (FinanceGroup $group) use ($amount, $siteId, $paymentMethod) {
            if (!$group->isActiveForSite($siteId)) {
                return false;
            }

            if ($paymentMethod === 'bank') {
                return $group->isAmountInRangeForSite($amount, $siteId);
            }

            if (!$this->isFinanceGroupCryptoEnabled($group)) {
                return false;
            }

            if (!$this->financeGroupHasActiveCryptoAccount((int) $group->id)) {
                return false;
            }

            return $group->isAmountInRangeForSite($amount, $siteId);
        });
    }

    private function financeGroupHasActiveCryptoAccount(int $financeGroupId): bool
    {
        static $cache = [];

        if (!array_key_exists($financeGroupId, $cache)) {
            $cache[$financeGroupId] = CryptoAccount::query()
                ->where('finance_group_id', $financeGroupId)
                ->where('is_active', true)
                ->whereNotNull('address')
                ->where('address', '!=', '')
                ->exists();
        }

        return (bool) $cache[$financeGroupId];
    }

    private function isFinanceGroupCryptoEnabled(FinanceGroup $group): bool
    {
        if (!Schema::hasColumn('finance_groups', 'is_crypto_enabled')) {
            return true;
        }

        return (bool) $group->is_crypto_enabled;
    }

    private function resolveCoinIconUrl(?string $icon): ?string
    {
        $icon = trim((string) $icon);
        if ($icon === '') {
            return null;
        }

        if (preg_match('/^https?:\/\//i', $icon)) {
            return $icon;
        }

        if (str_starts_with($icon, '/')) {
            return $icon;
        }

        if (!str_contains($icon, '/')) {
            return '/coins/' . ltrim($icon, '/');
        }

        return '/' . ltrim($icon, '/');
    }
}

