<?php

namespace App\Http\Controllers\Backoffice;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Transaction;
use App\Models\FinanceGroup;
use App\Models\CannedResponse;
use App\Models\ActivityLog;
use App\Models\Site;
use App\Support\SiteCredentialResolver;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Carbon\Carbon;
use App\Support\BusinessTime;

class DepositController extends Controller
{
    public function index(Request $request)
    {
        $query = Transaction::deposits()->with(['financeGroup', 'bankAccount', 'approvedBy', 'site']);
        $forcedSite = null;
        $routeSite = $request->route('site');
        if ($routeSite === null && $request->segment(2) === 'sites' && $request->segment(4) === 'deposits') {
            $routeSite = $request->segment(3);
        }
        if ($routeSite !== null) {
            if ($routeSite instanceof Site) {
                $forcedSite = $routeSite;
            } elseif (is_numeric($routeSite)) {
                $forcedSite = Site::find((int) $routeSite);
            } else {
                $forcedSite = Site::where('sid', (string) $routeSite)->first();
            }

            if (!$forcedSite) {
                abort(404);
            }
        }

        $isSiteScoped = $forcedSite !== null;
        $forcedSiteId = $forcedSite ? (int) $forcedSite->id : null;

        $siteFilterId = $forcedSite
            ? (int) $forcedSite->id
            : ($request->filled('site_id') ? (int) $request->site_id : null);

        // Apply filters
        if ($request->filled('status')) {
            if ($request->status === 'approved') {
                $query->whereIn('status', ['approved', 'completed']);
            } elseif ($request->status === 'declined') {
                $query->whereIn('status', ['declined', 'failed']);
            } else {
                $query->where('status', $request->status);
            }
        }

        if ($request->filled('finance_group_id')) {
            $query->where('finance_group_id', $request->finance_group_id);
        }

        if ($siteFilterId) {
            SiteCredentialResolver::applySiteFilter($query, $siteFilterId);
        }
        $dateMode = $request->input('date_mode', 'gaming');
        if ($request->filled('date_from') || $request->filled('date_to')) {
            $fromInput = $request->filled('date_from') ? $request->date_from : $request->date_to;
            $toInput = $request->filled('date_to') ? $request->date_to : $request->date_from;
            $fromHasTime = is_string($fromInput) && preg_match('/\d{2}:\d{2}/', $fromInput);
            $toHasTime = is_string($toInput) && preg_match('/\d{2}:\d{2}/', $toInput);
            if ($dateMode === 'actual') {
                $from = $fromHasTime
                    ? Carbon::parse($fromInput, BusinessTime::timezone())
                    : Carbon::parse($fromInput, BusinessTime::timezone())->startOfDay();
                $to = $toHasTime
                    ? Carbon::parse($toInput, BusinessTime::timezone())
                    : Carbon::parse($toInput, BusinessTime::timezone())->endOfDay();
            } else {
                $from = $fromHasTime
                    ? Carbon::parse($fromInput, BusinessTime::timezone())
                    : BusinessTime::startOfBusinessDay(Carbon::parse($fromInput, BusinessTime::timezone())->format('Y-m-d'));
                $to = $toHasTime
                    ? Carbon::parse($toInput, BusinessTime::timezone())
                    : BusinessTime::endOfBusinessDay(Carbon::parse($toInput, BusinessTime::timezone())->format('Y-m-d'));
            }
        } else {
            if ($dateMode === 'actual') {
                $to = Carbon::now(BusinessTime::timezone());
                $from = (clone $to)->subHours(24);
            } else {
                $businessDate = BusinessTime::currentBusinessDate()->format('Y-m-d');
                $from = BusinessTime::startOfBusinessDay($businessDate);
                $to = BusinessTime::endOfBusinessDay($businessDate);
            }
        }
        $query->whereBetween('created_at', [$from, $to]);

        if ($request->filled('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('transaction_id', 'like', "%{$search}%")
                    ->orWhere('customer_name', 'like', "%{$search}%")
                    ->orWhere('customer_player_id', 'like', "%{$search}%")
                    ->orWhere('customer_tc', 'like', "%{$search}%")
                    ->orWhere('customer_iban', 'like', "%{$search}%")
                    ->orWhere('sid', 'like', "%{$search}%")
                    ->orWhere('api_key', 'like', "%{$search}%");
            });
        }

        // For DataTables AJAX request
        if ($request->ajax()) {
            $deposits = $query->orderBy('created_at', 'desc')->get();
            $sites = Site::all();

            return response()->json([
                'data' => $deposits->map(function ($transaction) use ($sites) {
                    $resolvedSite = SiteCredentialResolver::resolveForTransaction($transaction, $sites);
                    $paymentMeta = $this->extractPaymentMeta($transaction);
                    return [
                        'id' => $transaction->id,
                        'transaction_id' => $transaction->transaction_id,
                        'status' => $transaction->status,
                        'amount' => number_format($transaction->amount, 2),
                        'currency' => $transaction->currency,
                        'amount_changed_from' => $transaction->amount_changed_from,
                        'amount_changed_at' => $transaction->amount_changed_at ? $transaction->amount_changed_at->toIso8601String() : null,
                        'amount_changed_by_name' => $transaction->amount_changed_by_name,
                        'customer_name' => $transaction->customer_name,
                        'customer_player_id' => $transaction->customer_player_id,
                        'customer_iban' => $transaction->customer_iban,
                        'customer_bank_name' => $transaction->customer_bank_name,
                        'source' => $transaction->source ?? 'auto',
                        'created_by_name' => $transaction->created_by_name,
                        'site_id' => $resolvedSite ? $resolvedSite->id : null,
                        'site_name' => $resolvedSite ? $resolvedSite->name : '-',
                        'description' => $transaction->description,
                        'payment_method' => $paymentMeta['payment_method'],
                        'payment_label' => $paymentMeta['payment_label'],
                        'is_crypto' => $paymentMeta['is_crypto'],
                        'crypto_account_id' => $paymentMeta['crypto_account_id'],
                        'crypto_coin_name' => $paymentMeta['crypto_coin_name'],
                        'crypto_address' => $paymentMeta['crypto_address'],
                        'finance_group' => $transaction->financeGroup ? $transaction->financeGroup->name : '-',
                        'finance_group_id' => $transaction->finance_group_id,
                        'finance_group_color' => $transaction->financeGroup ? $transaction->financeGroup->badge_color : null,
                        'bank_account' => $transaction->bankAccount ? $transaction->bankAccount->account_name : '-',
                        'bank_account_iban' => $transaction->bankAccount ? $transaction->bankAccount->iban : null,
                        'created_at' => $transaction->created_at->format('d.m.Y H:i'),
                        'created_at_timestamp' => $transaction->created_at->timestamp,
                        'actions' => true,
                    ];
                })
            ]);
        }

        // Get finance groups for filter
        $financeGroups = FinanceGroup::all();
        $sites = Site::orderBy('name')->get();

        // Get statistics for deposits only
        $confirmedStatuses = ['approved', 'completed'];
        $declinedStatuses = ['declined', 'failed'];

        $statsBase = Transaction::deposits()->whereBetween('created_at', [$from, $to]);
        if ($siteFilterId) {
            SiteCredentialResolver::applySiteFilter($statsBase, $siteFilterId);
        }
        $stats = [
            'total' => (clone $statsBase)->count(),
            'pending' => (clone $statsBase)->pending()->count(),
            'approved' => (clone $statsBase)->whereIn('status', $confirmedStatuses)->count(),
            'declined' => (clone $statsBase)->whereIn('status', $declinedStatuses)->count(),
            'total_amount' => (clone $statsBase)->whereIn('status', $confirmedStatuses)->sum('amount'),
        ];

        $cannedResponses = CannedResponse::orderBy('id', 'desc')->get();

        return view('backoffice.deposits.index', compact('financeGroups', 'sites', 'stats', 'cannedResponses', 'forcedSite', 'isSiteScoped', 'forcedSiteId'));
    }

    public function store(Request $request)
    {
        $request->validate([
            'customer_player_id' => 'required|string|max:255',
            'customer_name' => 'required|string|max:255',
            'customer_account_holder' => 'nullable|string|max:255',
            'customer_email' => 'nullable|email',
            'customer_phone' => 'nullable|string|max:20',
            'amount' => 'required|numeric|min:1',
            'site_id' => 'required|exists:sites,id',
            'finance_group_id' => 'required|exists:finance_groups,id',
            'iban' => 'nullable|string|max:34',
            'description' => 'nullable|string|max:1000',
            'notes' => 'nullable|string',
        ], [
            'customer_player_id.required' => 'Uye ID gereklidir.',
            'customer_name.required' => 'Uye adi soyadi gereklidir.',
            'amount.required' => 'Tutar gereklidir.',
            'amount.min' => 'Tutar 0 dan buyuk olmali.',
            'site_id.required' => 'Site secilmelidir.',
            'finance_group_id.required' => 'Finans grubu secilmelidir.',
        ]);

        DB::beginTransaction();
        try {
            $site = Site::findOrFail((int) $request->site_id);
            if (!$site->is_active) {
                throw new \RuntimeException('Secilen site pasif durumda.');
            }

            // Generate unique transaction ID
            $transactionId = Transaction::generateUniqueTransactionId('D');
            $transactionPayload = [
                'transaction_id' => $transactionId,
                'type' => 'deposit',
                'source' => 'manual',
                'status' => 'pending',
                'amount' => $request->amount,
                'currency' => 'TRY',
                'site_id' => $site->id,
                'sid' => $site->sid,
                'api_key' => $site->api_key,
                'customer_player_id' => $request->customer_player_id,
                'customer_name' => $request->customer_name,
                'customer_account_holder' => $request->customer_account_holder,
                'customer_iban' => $request->iban,
                'customer_email' => $request->customer_email,
                'customer_phone' => $request->customer_phone,
                'finance_group_id' => $request->finance_group_id,
                'bank_account_id' => null,
                'ip_address' => $request->ip(),
                'description' => $request->description,
                'notes' => $request->notes,
                'created_by_name' => auth()->user()->full_name ?? auth()->user()->name ?? null,
            ];

            $transactionPayload = $this->withTransactionPaymentPayload($transactionPayload, 'bank');
            $transaction = Transaction::create($transactionPayload);

            ActivityLog::log(
                'create_deposit',
                'deposits',
                "Yeni yatırım oluşturuldu: {$transaction->transaction_id} - {$transaction->customer_name} - " . number_format($transaction->amount, 2) . " TRY",
                null,
                $transaction->toArray()
            );

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Yatırım başarıyla oluşturuldu.',
                'transaction' => $transaction,
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Yatırım oluşturulurken bir hata oluştu: ' . $e->getMessage(),
            ], 500);
        }
    }

    public function show($id)
    {
        $deposit = Transaction::deposits()->with(['financeGroup', 'bankAccount', 'approvedBy', 'site'])->findOrFail($id);
        $deposit->setAttribute('created_at_display', $deposit->created_at->format('d.m.Y H:i'));
        $deposit->setAttribute('site_name', optional(SiteCredentialResolver::resolveForTransaction($deposit))->name);
        $paymentMeta = $this->extractPaymentMeta($deposit);
        $deposit->setAttribute('payment_method', $paymentMeta['payment_method']);
        $deposit->setAttribute('payment_label', $paymentMeta['payment_label']);
        $deposit->setAttribute('is_crypto', $paymentMeta['is_crypto']);
        $deposit->setAttribute('crypto_account_id', $paymentMeta['crypto_account_id']);
        $deposit->setAttribute('crypto_coin_name', $paymentMeta['crypto_coin_name']);
        $deposit->setAttribute('crypto_address', $paymentMeta['crypto_address']);

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

    public function approve(Request $request, $id)
    {
        $deposit = Transaction::deposits()->findOrFail($id);

        if ($deposit->status !== 'pending') {
            return response()->json([
                'success' => false,
                'message' => 'Bu yatırım zaten işleme alınmış.',
            ], 400);
        }

        DB::beginTransaction();
        try {
            $oldStatus = $deposit->status;

            $deposit->update([
                'status' => 'approved',
                'approved_by' => auth()->id(),
                'approved_by_name' => auth()->user()->full_name ?? auth()->user()->name ?? null,
                'approved_at' => now(),
                'notes' => $request->notes,
            ]);

            ActivityLog::log(
                'approve_deposit',
                'deposits',
                "Yatırım onaylandı: {$deposit->transaction_id}",
                ['status' => $oldStatus],
                ['status' => 'approved', 'notes' => $request->notes]
            );

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Yatırım başarıyla onaylandı.',
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Yatırım onaylanırken bir hata oluştu: ' . $e->getMessage(),
            ], 500);
        }
    }

    public function decline(Request $request, $id)
    {
        $request->validate([
            'notes' => 'required|string|min:3',
        ], [
            'notes.required' => 'Red nedeni gereklidir.',
            'notes.min' => 'Red nedeni en az 3 karakter olmalıdır.',
        ]);

        $deposit = Transaction::deposits()->findOrFail($id);

        if ($deposit->status !== 'pending') {
            return response()->json([
                'success' => false,
                'message' => 'Bu yatırım zaten işleme alınmış.',
            ], 400);
        }

        DB::beginTransaction();
        try {
            $oldStatus = $deposit->status;

            $deposit->update([
                'status' => 'declined',
                'approved_by' => auth()->id(),
                'approved_by_name' => auth()->user()->full_name ?? auth()->user()->name ?? null,
                'approved_at' => now(),
                'notes' => $request->notes,
            ]);

            ActivityLog::log(
                'decline_deposit',
                'deposits',
                "Yatırım reddedildi: {$deposit->transaction_id}",
                ['status' => $oldStatus],
                ['status' => 'declined', 'notes' => $request->notes]
            );

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Yatırım başarıyla reddedildi.',
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Yatırım reddedilirken bir hata oluştu: ' . $e->getMessage(),
            ], 500);
        }
    }

    public function complete(Request $request, $id)
    {
        $deposit = Transaction::deposits()->findOrFail($id);

        if ($deposit->status !== 'approved') {
            return response()->json([
                'success' => false,
                'message' => 'Sadece onaylanmış yatırımlar tamamlanabilir.',
            ], 400);
        }

        DB::beginTransaction();
        try {
            $oldStatus = $deposit->status;

            $deposit->update([
                'status' => 'completed',
                'notes' => $request->notes,
            ]);

            ActivityLog::log(
                'complete_deposit',
                'deposits',
                "Yatırım tamamlandı: {$deposit->transaction_id}",
                ['status' => $oldStatus],
                ['status' => 'completed', 'notes' => $request->notes]
            );

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Yatırım başarıyla tamamlandı.',
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Yatırım tamamlanırken bir hata oluştu: ' . $e->getMessage(),
            ], 500);
        }
    }

    public function stats(Request $request)
    {
        $query = Transaction::deposits();

        if ($request->filled('finance_group_id')) {
            $query->where('finance_group_id', $request->finance_group_id);
        }
        if ($request->filled('site_id')) {
            SiteCredentialResolver::applySiteFilter($query, (int) $request->site_id);
        }
        $dateMode = $request->input('date_mode', 'gaming');
        if ($request->filled('date_from') || $request->filled('date_to')) {
            $fromInput = $request->filled('date_from') ? $request->date_from : $request->date_to;
            $toInput = $request->filled('date_to') ? $request->date_to : $request->date_from;
            $fromHasTime = is_string($fromInput) && preg_match('/\d{2}:\d{2}/', $fromInput);
            $toHasTime = is_string($toInput) && preg_match('/\d{2}:\d{2}/', $toInput);
            if ($dateMode === 'actual') {
                $from = $fromHasTime
                    ? Carbon::parse($fromInput, BusinessTime::timezone())
                    : Carbon::parse($fromInput, BusinessTime::timezone())->startOfDay();
                $to = $toHasTime
                    ? Carbon::parse($toInput, BusinessTime::timezone())
                    : Carbon::parse($toInput, BusinessTime::timezone())->endOfDay();
            } else {
                $from = $fromHasTime
                    ? Carbon::parse($fromInput, BusinessTime::timezone())
                    : BusinessTime::startOfBusinessDay(Carbon::parse($fromInput, BusinessTime::timezone())->format('Y-m-d'));
                $to = $toHasTime
                    ? Carbon::parse($toInput, BusinessTime::timezone())
                    : BusinessTime::endOfBusinessDay(Carbon::parse($toInput, BusinessTime::timezone())->format('Y-m-d'));
            }
        } else {
            if ($dateMode === 'actual') {
                $to = Carbon::now(BusinessTime::timezone());
                $from = (clone $to)->subHours(24);
            } else {
                $businessDate = BusinessTime::currentBusinessDate()->format('Y-m-d');
                $from = BusinessTime::startOfBusinessDay($businessDate);
                $to = BusinessTime::endOfBusinessDay($businessDate);
            }
        }
        $query->whereBetween('created_at', [$from, $to]);

        if ($request->filled('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('transaction_id', 'like', "%{$search}%")
                    ->orWhere('customer_name', 'like', "%{$search}%")
                    ->orWhere('customer_player_id', 'like', "%{$search}%")
                    ->orWhere('customer_tc', 'like', "%{$search}%")
                    ->orWhere('customer_iban', 'like', "%{$search}%")
                    ->orWhere('sid', 'like', "%{$search}%")
                    ->orWhere('api_key', 'like', "%{$search}%");
            });
        }

        $confirmedStatuses = ['approved', 'completed'];
        $declinedStatuses = ['declined', 'failed'];

        return response()->json([
            'total' => (clone $query)->count(),
            'pending' => (clone $query)->where('status', 'pending')->count(),
            'approved' => (clone $query)->whereIn('status', $confirmedStatuses)->count(),
            'declined' => (clone $query)->whereIn('status', $declinedStatuses)->count(),
        ]);
    }

    public function checkNew(Request $request)
    {
        $since = $request->input('since');

        if (!$since || !is_numeric($since)) {
            return response()->json([
                'success' => false,
                'message' => 'Timestamp parametresi gerekli.',
            ], 400);
        }

        $sinceDate = Carbon::createFromTimestamp((int) $since);

        $baseQuery = Transaction::deposits()->with('site');
        if ($request->filled('site_id')) {
            SiteCredentialResolver::applySiteFilter($baseQuery, (int) $request->site_id);
        }

        $updatedCount = (clone $baseQuery)
            ->where('updated_at', '>', $sinceDate)
            ->count();

        $latestUpdate = (clone $baseQuery)->max('updated_at');
        $latestTimestamp = $latestUpdate ? Carbon::parse($latestUpdate)->timestamp : (int) $since;

        $newDeposits = (clone $baseQuery)
            ->pending()
            ->with(['financeGroup', 'bankAccount', 'approvedBy'])
            ->where('created_at', '>', $sinceDate)
            ->orderBy('created_at', 'desc')
            ->get()
            ->map(function ($transaction) {
                $resolvedSite = SiteCredentialResolver::resolveForTransaction($transaction);
                return [
                    'id' => $transaction->id,
                    'transaction_id' => $transaction->transaction_id,
                    'status' => $transaction->status,
                    'amount' => number_format($transaction->amount, 2),
                    'currency' => $transaction->currency,
                    'customer_name' => $transaction->customer_name,
                    'site_name' => $resolvedSite ? $resolvedSite->name : '-',
                    'finance_group' => $transaction->financeGroup ? $transaction->financeGroup->name : '-',
                    'finance_group_id' => $transaction->finance_group_id,
                    'finance_group_color' => $transaction->financeGroup ? $transaction->financeGroup->badge_color : null,
                    'bank_account' => $transaction->bankAccount ? $transaction->bankAccount->account_name : '-',
                    'created_at' => $transaction->created_at->format('d.m.Y H:i'),
                    'created_at_timestamp' => $transaction->created_at->timestamp,
                    'actions' => true,
                ];
            });

        return response()->json([
            'success' => true,
            'count' => $newDeposits->count(),
            'data' => $newDeposits,
            'updated_count' => $updatedCount,
            'latest_timestamp' => $latestTimestamp,
        ]);
    }

    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 extractPaymentMeta(Transaction $transaction): array
    {
        $paymentMethod = 'bank';
        $cryptoCoinName = null;
        $cryptoAddress = null;
        $cryptoAccountId = null;

        if ($this->transactionColumnExists('payment_method') && (string) $transaction->payment_method === 'crypto') {
            $paymentMethod = 'crypto';
        } elseif ($this->transactionColumnExists('is_crypto') && (bool) $transaction->is_crypto) {
            $paymentMethod = 'crypto';
        } elseif ($this->parseCryptoDescription((string) $transaction->description) !== null) {
            $paymentMethod = 'crypto';
        }

        if ($paymentMethod === 'crypto') {
            if ($this->transactionColumnExists('crypto_coin_name')) {
                $cryptoCoinName = $transaction->crypto_coin_name;
            }
            if ($this->transactionColumnExists('crypto_address')) {
                $cryptoAddress = $transaction->crypto_address;
            }
            if ($this->transactionColumnExists('crypto_account_id')) {
                $cryptoAccountId = $transaction->crypto_account_id ? (int) $transaction->crypto_account_id : null;
            }

            if (!$cryptoCoinName || !$cryptoAddress) {
                $parsed = $this->parseCryptoDescription((string) $transaction->description);
                if ($parsed) {
                    $cryptoCoinName = $cryptoCoinName ?: $parsed['coin_name'];
                    $cryptoAddress = $cryptoAddress ?: $parsed['address'];
                }
            }
        }

        return [
            'payment_method' => $paymentMethod,
            'payment_label' => $paymentMethod === 'crypto' ? 'Kripto' : 'Banka',
            'is_crypto' => $paymentMethod === 'crypto',
            'crypto_account_id' => $cryptoAccountId,
            'crypto_coin_name' => $cryptoCoinName,
            'crypto_address' => $cryptoAddress,
        ];
    }

    private function parseCryptoDescription(string $description): ?array
    {
        $description = trim($description);
        if ($description === '') {
            return null;
        }

        if (!preg_match('/^KRIPTO\s+(.+?)\s*-\s*(.+)$/iu', $description, $matches)) {
            return null;
        }

        return [
            'coin_name' => trim((string) ($matches[1] ?? '')),
            'address' => trim((string) ($matches[2] ?? '')),
        ];
    }

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

        return $columns[$column];
    }
}
