<?php

namespace App\Http\Controllers;

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

class AgentBalanceController extends Controller
{
    public function index(Request $request)
    {
        $filters = $request->only(['search', 'agent_id', 'date_from', 'date_to']);

        $query = AgentBalance::with(['agent', 'account.currency', 'transaction']);

        if ($request->filled('search')) {
            $search = $request->input('search');
            $query->where(function($q) use ($search) {
                $q->where('description', 'like', "%{$search}%")
                  ->orWhereHas('agent', function($sq) use ($search) {
                      $sq->where('name', 'like', "%{$search}%");
                  })
                  ->orWhereHas('account', function($sq) use ($search) {
                      $sq->where('name', 'like', "%{$search}%")
                        ->orWhereHas('currency', function($ssq) use ($search) {
                            $ssq->where('code', 'like', "%{$search}%");
                        });
                  });
            });
        }

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

        if ($request->filled('date_from')) {
            $query->whereDate('date', '>=', $request->input('date_from'));
        }

        if ($request->filled('date_to')) {
            $query->whereDate('date', '<=', $request->input('date_to'));
        }

        $balances = $query->latest('date')->get();

        $agents = Agent::orderBy('name')->get();
        $accounts = Account::with('currency')->orderBy('name')->get();

        return view('agent_balances.index', compact('balances', 'agents', 'accounts', 'filters'));
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'agent_id' => ['required', 'exists:agents,id'],
            'account_id' => ['required', 'exists:accounts,id'],
            'quantity' => ['required', 'numeric', 'gt:0'],
            'rate' => ['required', 'numeric', 'gt:0'],
            'date' => ['required', 'date'],
            'description' => ['nullable', 'string', 'max:255'],
        ]);

        AgentBalance::create([
            'agent_id' => $data['agent_id'],
            'account_id' => $data['account_id'],
            'quantity' => $data['quantity'],
            'rate' => $data['rate'],
            'date' => $data['date'],
            'description' => $data['description'] ?? null,
            'is_received' => false,
            'received_at' => null,
            'transaction_id' => null,
        ]);

        return redirect()
            ->route('agent-balances.index')
            ->with('success', 'Agent balance record created successfully.');
    }

    public function edit(AgentBalance $agentBalance)
    {
        $agents = Agent::orderBy('name')->get();
        $accounts = Account::with('currency')->orderBy('name')->get();

        return view('agent_balances.edit', compact('agentBalance', 'agents', 'accounts'));
    }

    public function update(Request $request, AgentBalance $agentBalance)
    {
        $data = $request->validate([
            'agent_id' => ['required', 'exists:agents,id'],
            'account_id' => ['required', 'exists:accounts,id'],
            'quantity' => ['required', 'numeric', 'gt:0'],
            'rate' => ['required', 'numeric', 'gt:0'],
            'date' => ['required', 'date'],
            'description' => ['nullable', 'string', 'max:255'],
        ]);

        DB::transaction(function () use ($data, $agentBalance) {
            $agentBalance->lockForUpdate();

            if ($agentBalance->is_received && $agentBalance->transaction_id) {
                $transaction = Transaction::lockForUpdate()->findOrFail($agentBalance->transaction_id);

                // 1. Reverse old balance effects on target account
                $transaction->reverseBalanceEffects();

                // 2. Load old and new currency details
                $oldCurrency = $transaction->currency;
                
                $newAccount = Account::lockForUpdate()->findOrFail($data['account_id']);
                $newCurrency = $newAccount->currency;

                // 3. Update the transaction
                $transaction->fill([
                    'agent_id' => $data['agent_id'],
                    'currency_id' => $newCurrency->id,
                    'quantity' => (float) $data['quantity'],
                    'rate' => (float) $data['rate'],
                    'payment_amount' => (float) $data['quantity'] * (float) $data['rate'],
                    'account_id' => $newAccount->id,
                    'description' => $data['description'] ?? ("Agent Balance Received: " . Agent::find($data['agent_id'])->name),
                ]);
                $transaction->created_at = $data['date'];
                $transaction->updated_at = $data['date'];
                $transaction->save();

                // 4. Apply new balance effects (increment new account)
                $newAccount->increment('balance', (float) $data['quantity']);

                // 5. Recalculate profits
                $oldCurrency->recalculateProfits();
                if ($oldCurrency->id !== $newCurrency->id) {
                    $newCurrency->recalculateProfits();
                }
            }

            // Update the AgentBalance record itself
            $agentBalance->update([
                'agent_id' => $data['agent_id'],
                'account_id' => $data['account_id'],
                'quantity' => $data['quantity'],
                'rate' => $data['rate'],
                'date' => $data['date'],
                'description' => $data['description'] ?? null,
            ]);
        });

        return redirect()
            ->route('agent-balances.index')
            ->with('success', 'Agent balance record updated successfully.');
    }

    public function destroy(AgentBalance $agentBalance)
    {
        DB::transaction(function () use ($agentBalance) {
            $agentBalance->lockForUpdate();

            if ($agentBalance->is_received && $agentBalance->transaction_id) {
                $transaction = Transaction::lockForUpdate()->find($agentBalance->transaction_id);
                if ($transaction) {
                    $currency = $transaction->currency;
                    
                    // Reverse account balance effects
                    $transaction->reverseBalanceEffects();
                    
                    // Delete the transaction
                    $transaction->delete();

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

            $agentBalance->delete();
        });

        return redirect()
            ->route('agent-balances.index')
            ->with('success', 'Agent balance record deleted successfully.');
    }

    public function markReceived(AgentBalance $agentBalance)
    {
        DB::transaction(function () use ($agentBalance) {
            $agentBalance->lockForUpdate();

            if ($agentBalance->is_received) {
                throw ValidationException::withMessages([
                    'agent_balance' => 'This agent balance has already been marked as received.',
                ]);
            }

            $targetAccount = Account::lockForUpdate()->findOrFail($agentBalance->account_id);
            $agent = Agent::findOrFail($agentBalance->agent_id);

            // Increment receiving account's balance
            $targetAccount->increment('balance', (float) $agentBalance->quantity);

            // Create standard BUY transaction
            $transaction = new Transaction([
                'type' => Transaction::TYPE_BUY,
                'agent_id' => $agent->id,
                'currency_id' => $targetAccount->currency_id,
                'quantity' => $agentBalance->quantity,
                'rate' => $agentBalance->rate,
                'payment_amount' => (float) $agentBalance->quantity * (float) $agentBalance->rate,
                'payment_currency_id' => null, // No payment account/currency involved
                'payment_account_id' => null,
                'handling_fee' => 0.0000,
                'fee_currency' => $targetAccount->currency->code,
                'account_id' => $targetAccount->id,
                'is_pending' => false,
                'exchange_profit' => 0.0000,
                'total_profit' => 0.0000,
                'description' => $agentBalance->description ?? ("Agent Balance Received: " . $agent->name),
            ]);
            $transaction->created_at = $agentBalance->date;
            $transaction->updated_at = $agentBalance->date;
            $transaction->save();

            // Link agent balance to transaction
            $agentBalance->update([
                'is_received' => true,
                'received_at' => now(),
                'transaction_id' => $transaction->id,
            ]);

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

        return redirect()
            ->route('agent-balances.index')
            ->with('success', 'Agent balance marked as received successfully. Stock and average buy rate updated.');
    }
}
