<?php

namespace App\Http\Controllers;

use App\Models\Account;
use App\Models\Agent;
use App\Models\Currency;
use App\Models\Transaction;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class BuyCurrencyController extends Controller
{
    public function create()
    {
        return view('transactions.buy', [
            'agents' => Agent::orderBy('name')->get(),
            'currencies' => Currency::orderBy('code')->get(),
            'accounts' => Account::with('currency')->orderBy('name')->get(),
        ]);
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'agent_id' => ['nullable', 'exists:agents,id'],
            'currency_id' => ['required', 'exists:currencies,id'],
            'payment_account_id' => ['required', 'exists:accounts,id'],
            'target_account_id' => ['required', 'exists:accounts,id'],
            'quantity' => ['required', 'numeric', 'gt:0'],
            'payment_amount' => ['required', 'numeric', 'gt:0'],
            'handling_fee' => ['required', 'numeric', 'min:0'],
            'is_pending' => ['nullable', 'boolean'],
        ]);

        DB::transaction(function () use ($data) {
            $currency = Currency::lockForUpdate()->findOrFail($data['currency_id']);
            $targetAccount = Account::lockForUpdate()->findOrFail($data['target_account_id']);
            $paymentAccount = Account::with('currency')->lockForUpdate()->findOrFail($data['payment_account_id']);

            if ($targetAccount->currency_id !== $currency->id) {
                throw ValidationException::withMessages([
                    'target_account_id' => 'Target account currency must match the selected currency.',
                ]);
            }

            $quantity = (float) $data['quantity'];
            $paymentAmount = (float) $data['payment_amount'];
            $handlingFee = (float) $data['handling_fee'];
            $feeCurrency = strtoupper($targetAccount->currency->code);
            $isPending = !empty($data['is_pending']);

            // Calculate rate dynamically: rate = total paid / quantity
            $rate = round($paymentAmount / $quantity, 4);

            if ((float) $paymentAccount->balance < $paymentAmount) {
                throw ValidationException::withMessages([
                    'payment_account_id' => 'Selected payment account does not have enough balance.',
                ]);
            }

            // Deduct from payment account immediately
            $paymentAccount->update([
                'balance' => (float) $paymentAccount->balance - $paymentAmount,
            ]);

            // Add to target account (only if not pending)
            if (!$isPending) {
                $targetAccount->update([
                    'balance' => (float) $targetAccount->balance + $quantity + $handlingFee,
                ]);
            }

            Transaction::create([
                'type' => Transaction::TYPE_BUY,
                'agent_id' => $data['agent_id'] ?? null,
                'currency_id' => $currency->id,
                'quantity' => $quantity,
                'rate' => $rate,
                'payment_amount' => $paymentAmount,
                'payment_currency_id' => $paymentAccount->currency_id,
                'payment_account_id' => $paymentAccount->id,
                'handling_fee' => $handlingFee,
                'fee_currency' => $feeCurrency,
                'account_id' => $targetAccount->id,
                'is_pending' => $isPending,
                'exchange_profit' => 0,
                'total_profit' => 0,
            ]);
        });

        return redirect()
            ->route('transactions.buy.create')
            ->with('success', 'Buy transaction saved successfully.');
    }

    public function edit(Transaction $transaction)
    {
        if ($transaction->type !== Transaction::TYPE_BUY) {
            abort(404);
        }

        return view('transactions.buy_edit', [
            'transaction' => $transaction,
            'agents' => Agent::orderBy('name')->get(),
            'currencies' => Currency::orderBy('code')->get(),
            'accounts' => Account::with('currency')->orderBy('name')->get(),
        ]);
    }

    public function update(Request $request, Transaction $transaction)
    {
        if ($transaction->type !== Transaction::TYPE_BUY) {
            abort(404);
        }

        $data = $request->validate([
            'agent_id' => ['nullable', 'exists:agents,id'],
            'currency_id' => ['required', 'exists:currencies,id'],
            'payment_account_id' => ['required', 'exists:accounts,id'],
            'target_account_id' => ['required', 'exists:accounts,id'],
            'quantity' => ['required', 'numeric', 'gt:0'],
            'payment_amount' => ['required', 'numeric', 'gt:0'],
            'handling_fee' => ['required', 'numeric', 'min:0'],
            'is_pending' => ['nullable', 'boolean'],
        ]);

        DB::transaction(function () use ($data, $transaction) {
            // 1. Reverse old transaction
            $transaction->reverseBalanceEffects();

            // 2. Prepare new data
            $currency = Currency::lockForUpdate()->findOrFail($data['currency_id']);
            $targetAccount = Account::lockForUpdate()->findOrFail($data['target_account_id']);
            $paymentAccount = Account::with('currency')->lockForUpdate()->findOrFail($data['payment_account_id']);

            if ($targetAccount->currency_id !== $currency->id) {
                throw ValidationException::withMessages([
                    'target_account_id' => 'Target account currency must match the selected currency.',
                ]);
            }

            $quantity = (float) $data['quantity'];
            $paymentAmount = (float) $data['payment_amount'];
            $handlingFee = (float) $data['handling_fee'];
            $feeCurrency = strtoupper($targetAccount->currency->code);
            $isPending = !empty($data['is_pending']);

            // Calculate rate dynamically
            $rate = round($paymentAmount / $quantity, 4);

            if ((float) $paymentAccount->balance < $paymentAmount) {
                throw ValidationException::withMessages([
                    'payment_account_id' => 'Selected payment account does not have enough balance.',
                ]);
            }

            // 3. Deduct from payment account immediately
            $paymentAccount->update([
                'balance' => (float) $paymentAccount->balance - $paymentAmount,
            ]);

            // 4. Add to target account (only if not pending)
            if (!$isPending) {
                $targetAccount->update([
                    'balance' => (float) $targetAccount->balance + $quantity + $handlingFee,
                ]);
            }

            // 5. Update transaction record
            $transaction->update([
                'agent_id' => $data['agent_id'] ?? null,
                'currency_id' => $currency->id,
                'quantity' => $quantity,
                'rate' => $rate,
                'payment_amount' => $paymentAmount,
                'payment_currency_id' => $paymentAccount->currency_id,
                'payment_account_id' => $paymentAccount->id,
                'handling_fee' => $handlingFee,
                'fee_currency' => $feeCurrency,
                'account_id' => $targetAccount->id,
                'is_pending' => $isPending,
            ]);

            // 6. Recalculate profits
            $currency->recalculateProfits();
        });

        return redirect()
            ->route('reports.transactions')
            ->with('success', 'Buy transaction updated and balances recalculated successfully.');
    }
}

