<?php

namespace App\Http\Controllers;

use App\Enums\RequestStatus;
use App\Enums\TransactionSource;
use App\Enums\TransactionStatus;
use App\Enums\TransactionType;
use App\Enums\TransferType;
use App\Enums\TransactionCategory;
use App\Http\Requests\InternalTransferRequest;
use App\Http\Requests\InternationalTransferRequest;
use App\Http\Requests\LocalTransferRequest;
use App\Models\Account;
use App\Models\Transaction;
use App\Models\TransferRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class TransferController extends Controller
{
    public function show()
    {
        try {
            $accounts = auth()->user()->accounts;
            $recentRecipients = TransferRequest::where('from_account_id', '!=', null)
                ->whereIn('from_account_id', $accounts->pluck('id'))
                ->latest()
                ->take(5)
                ->get();

            return view('transfer.index', [
                'accounts' => $accounts,
                'recentRecipients' => $recentRecipients,
                'activePage' => 'transfer',
            ]);
        } catch (\Exception $e) {
            Log::error($e->getMessage());
            return back()->with('alert', 'An error occurred.');
        }
    }

    public function internal(InternalTransferRequest $request)
    {
        try {
            if (!Hash::check($request->password, auth()->user()->password)) {
                return back()->withErrors(['password' => 'Invalid password.']);
            }

            $fromAccount = Account::where('id', $request->from_account_id)
                ->where('user_id', auth()->id())
                ->firstOrFail();

            $toAccount = Account::where('account_number', $request->to_identifier)
                ->orWhereHas('user', fn($q) => $q->where('email', $request->to_identifier))
                ->first();

            if (!$toAccount) {
                return back()->withErrors(['to_identifier' => 'Recipient not found.']);
            }

            if ($fromAccount->balance < $request->amount) {
                return back()->withErrors(['amount' => 'Insufficient balance.']);
            }

            $ref = 'TXN-' . strtoupper(Str::random(12));

            DB::transaction(function () use ($fromAccount, $toAccount, $request, $ref) {
                $fromAccount->decrement('balance', $request->amount);
                $toAccount->increment('balance', $request->amount);

                $category = $request->category ? TransactionCategory::from($request->category) : TransactionCategory::Transfer;

                Transaction::create([
                    'account_id' => $fromAccount->id,
                    'type' => TransactionType::Debit,
                    'amount' => $request->amount,
                    'balance_after' => $fromAccount->fresh()->balance,
                    'description' => 'Transfer to ' . $toAccount->account_number,
                    'category' => $category,
                    'status' => TransactionStatus::Completed,
                    'source' => TransactionSource::InternalTransfer,
                    'reference' => $ref,
                ]);

                Transaction::create([
                    'account_id' => $toAccount->id,
                    'type' => TransactionType::Credit,
                    'amount' => $request->amount,
                    'balance_after' => $toAccount->fresh()->balance,
                    'description' => 'Transfer from ' . $fromAccount->account_number,
                    'category' => $category,
                    'status' => TransactionStatus::Completed,
                    'source' => TransactionSource::InternalTransfer,
                    'reference' => 'TXN-' . strtoupper(Str::random(12)),
                ]);

                TransferRequest::create([
                    'from_account_id' => $fromAccount->id,
                    'type' => TransferType::Internal,
                    'to_account_id' => $toAccount->id,
                    'amount' => $request->amount,
                    'currency' => $fromAccount->currency->value,
                    'description' => $request->description,
                    'status' => RequestStatus::Approved,
                ]);
            });

            $debitTransaction = Transaction::where('reference', $ref)->first();
            return redirect()->route('transactions.show', $debitTransaction)->with('success', 'Transfer completed successfully.');
        } catch (\Exception $e) {
            Log::error($e->getMessage());
            return back()->with('alert', 'An error occurred.');
        }
    }

    public function wireTransfer(InternationalTransferRequest $request)
    {
        try {
            if (!Hash::check($request->password, auth()->user()->password)) {
                return back()->withErrors(['password' => 'Invalid password.']);
            }

            $fromAccount = Account::where('id', $request->from_account_id)
                ->where('user_id', auth()->id())
                ->firstOrFail();

            if ($fromAccount->balance < $request->amount) {
                return back()->withErrors(['amount' => 'Insufficient balance.']);
            }

            $fee = round($request->amount * 0.015, 2); // 1.5% fee
            $intRef = 'TXN-' . strtoupper(Str::random(12));

            DB::transaction(function () use ($fromAccount, $request, $fee, $intRef) {
                $totalDebit = $request->amount + $fee;
                $fromAccount->decrement('balance', $totalDebit);

                $category = $request->category ? TransactionCategory::from($request->category) : TransactionCategory::Transfer;

                Transaction::create([
                    'account_id' => $fromAccount->id,
                    'type' => TransactionType::Debit,
                    'amount' => $totalDebit,
                    'balance_after' => $fromAccount->fresh()->balance,
                    'description' => 'Wire transfer to ' . $request->recipient_name,
                    'category' => $category,
                    'status' => TransactionStatus::Pending,
                    'source' => TransactionSource::WireTransfer,
                    'reference' => $intRef,
                ]);

                TransferRequest::create([
                    'from_account_id' => $fromAccount->id,
                    'type' => TransferType::WireTransfer,
                    'recipient_details' => [
                        'name' => $request->recipient_name,
                        'bank_name' => $request->bank_name,
                        'swift_code' => $request->swift_code,
                        'iban' => $request->iban,
                        'routing_number' => $request->routing_number,
                        'country' => $request->recipient_country,
                    ],
                    'amount' => $request->amount,
                    'currency' => $request->currency,
                    'fee' => $fee,
                    'description' => $request->description,
                    'status' => RequestStatus::Pending,
                ]);
            });

            $transaction = Transaction::where('reference', $intRef)->first();

            return redirect()->route('transactions.show', $transaction)->with('success', 'Wire transfer submitted for processing.');
        } catch (\Exception $e) {
            Log::error($e->getMessage());
            return back()->with('alert', 'An error occurred.');
        }
    }

    public function local(LocalTransferRequest $request)
    {
        try {
            if (!Hash::check($request->password, auth()->user()->password)) {
                return back()->withErrors(['password' => 'Invalid password.']);
            }

            $fromAccount = Account::where('id', $request->from_account_id)
                ->where('user_id', auth()->id())
                ->firstOrFail();

            if ($fromAccount->balance < $request->amount) {
                return back()->withErrors(['amount' => 'Insufficient balance.']);
            }

            $fee = round($request->amount * 0.005, 2); // 0.5% fee
            $ref = 'TXN-' . strtoupper(Str::random(12));

            // Check if recipient exists in the system by account number + routing number
            $toAccount = Account::where('account_number', $request->account_number)
                ->whereHas('routingNumbers', fn($q) => $q->where('routing_number', $request->routing_number))
                ->first();

            $isInternal = $toAccount !== null;
            $category = $request->category ? TransactionCategory::from($request->category) : TransactionCategory::Transfer;

            DB::transaction(function () use ($fromAccount, $toAccount, $request, $fee, $ref, $isInternal, $category) {
                $totalDebit = $request->amount + $fee;
                $fromAccount->decrement('balance', $totalDebit);

                $status = $isInternal ? TransactionStatus::Completed : TransactionStatus::Pending;

                Transaction::create([
                    'account_id' => $fromAccount->id,
                    'type' => TransactionType::Debit,
                    'amount' => $totalDebit,
                    'balance_after' => $fromAccount->fresh()->balance,
                    'description' => 'Local transfer to ' . $request->recipient_name,
                    'category' => $category,
                    'status' => $status,
                    'source' => TransactionSource::LocalTransfer,
                    'reference' => $ref,
                ]);

                if ($isInternal) {
                    $toAccount->increment('balance', $request->amount);

                    Transaction::create([
                        'account_id' => $toAccount->id,
                        'type' => TransactionType::Credit,
                        'amount' => $request->amount,
                        'balance_after' => $toAccount->fresh()->balance,
                        'description' => 'Local transfer from ' . $fromAccount->account_number,
                        'category' => $category,
                        'status' => TransactionStatus::Completed,
                        'source' => TransactionSource::LocalTransfer,
                        'reference' => 'TXN-' . strtoupper(Str::random(12)),
                    ]);
                }

                TransferRequest::create([
                    'from_account_id' => $fromAccount->id,
                    'type' => TransferType::Local,
                    'to_account_id' => $isInternal ? $toAccount->id : null,
                    'recipient_details' => [
                        'name' => $request->recipient_name,
                        'account_number' => $request->account_number,
                        'routing_number' => $request->routing_number,
                    ],
                    'amount' => $request->amount,
                    'currency' => $fromAccount->currency->value,
                    'fee' => $fee,
                    'description' => $request->description,
                    'status' => $isInternal ? RequestStatus::Approved : RequestStatus::Pending,
                ]);
            });

            $transaction = Transaction::where('reference', $ref)->first();
            $message = $isInternal
                ? 'Local transfer completed successfully.'
                : 'Local transfer submitted for processing.';

            return redirect()->route('transactions.show', $transaction)->with('success', $message);
        } catch (\Exception $e) {
            Log::error($e->getMessage());
            return back()->with('alert', 'An error occurred.');
        }
    }

    public function lookup(Request $request)
    {
        try {
            $q = $request->get('q');
            if (!$q || strlen($q) < 3) {
                return response()->json([]);
            }

            $account = Account::where('account_number', 'like', "%{$q}%")
                ->with('user:id,first_name,last_name')
                ->first();

            if (!$account) {
                return response()->json([]);
            }

            return response()->json([
                'name' => $account->user->first_name . ' ' . $account->user->last_name,
                'account_number' => $account->account_number,
            ]);
        } catch (\Exception $e) {
            Log::error($e->getMessage());
            return response()->json(['error' => 'An error occurred.'], 500);
        }
    }
}
