<?php

namespace App\Http\Controllers;

use App\Models\Transaction;
use App\Models\BankAccount;
use App\Models\DigitalPayment;
use App\Models\ExpenseType;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class TransactionController extends Controller
{
    public function index(Request $request)
    {
        $query = Transaction::with(['bankAccount', 'digitalPayment', 'expenseType']);

        if (!auth()->user()->isSuperAdmin()) {
            $query->where('user_id', auth()->id());
        }

        // Filter by date range
        if ($request->filled('from_date')) {
            $query->whereDate('date', '>=', $request->from_date);
        }
        if ($request->filled('to_date')) {
            $query->whereDate('date', '<=', $request->to_date);
        }

        // Filter by type
        if ($request->filled('type')) {
            $query->where('type', $request->type);
        }

        // Filter by payment method
        if ($request->filled('payment_method')) {
            $query->where('payment_method', $request->payment_method);
        }

        $transactions = $query->orderBy('date', 'desc')
            ->orderBy('id', 'desc')
            ->paginate(20);

        $totalIncome = $query->clone()->where('type', 'income')->sum('amount');
        $totalExpense = $query->clone()->where('type', 'expense')->sum('amount');

        return view('transactions.index', compact('transactions', 'totalIncome', 'totalExpense'));
    }

    public function create()
    {
        if (auth()->user()->isSuperAdmin()) {
            $bankAccounts = BankAccount::where('is_active', true)->get();
            $digitalPayments = DigitalPayment::where('is_active', true)->get();
            $expenseTypes = ExpenseType::orderBy('name')->get();
        } else {
            $bankAccounts = auth()->user()->bankAccounts()->where('is_active', true)->get();
            $digitalPayments = auth()->user()->digitalPayments()->where('is_active', true)->get();
            $expenseTypes = ExpenseType::where(function ($query) {
                $query->where('user_id', auth()->id())
                    ->orWhere('is_system', true);
            })->orderBy('name')->get();
        }

        return view('transactions.create', compact('bankAccounts', 'digitalPayments', 'expenseTypes'));
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'date' => 'required|date',
            'type' => 'required|in:income,expense,transfer',
            'payment_method' => 'required|in:cash,bank,digital',
            // Only validate bank account when payment_method is bank
            'bank_account_id' => 'exclude_unless:payment_method,bank|required|exists:bank_accounts,id',
            // Only validate digital payment when payment_method is digital
            'digital_payment_id' => 'exclude_unless:payment_method,digital|required|exists:digital_payments,id',
            // Only validate expense type when type is expense
            'expense_type_id' => 'exclude_unless:type,expense|required|exists:expense_types,id',
            'amount' => 'required|numeric|min:0.01',
            'details' => 'nullable|string|max:500',
            'reference' => 'nullable|string|max:100',
            'notes' => 'nullable|string|max:500',
        ]);

        $user = auth()->user();

        DB::transaction(function () use ($validated, $user) {
            // Get current balance based on payment method
            $currentBalance = $this->getCurrentBalance($validated['payment_method'],
                $validated['bank_account_id'] ?? null,
                $validated['digital_payment_id'] ?? null);

            // Calculate new balance
            if ($validated['type'] === 'income') {
                $newBalance = $currentBalance + $validated['amount'];
            } else {
                $newBalance = $currentBalance - $validated['amount'];
            }

            // Create transaction
            $transaction = $user->transactions()->create([
                'date' => $validated['date'],
                'type' => $validated['type'],
                'payment_method' => $validated['payment_method'],
                'bank_account_id' => $validated['bank_account_id'] ?? null,
                'digital_payment_id' => $validated['digital_payment_id'] ?? null,
                'expense_type_id' => $validated['expense_type_id'] ?? null,
                'amount' => $validated['amount'],
                'balance_before' => $currentBalance,
                'balance_after' => $newBalance,
                'details' => $validated['details'] ?? null,
                'reference' => $validated['reference'] ?? null,
                'notes' => $validated['notes'] ?? null,
            ]);

            // Update account balance
            $this->updateAccountBalance($validated['payment_method'],
                $validated['bank_account_id'] ?? null,
                $validated['digital_payment_id'] ?? null,
                $newBalance);

            // Update user total balance
            $this->updateUserBalance($user, $validated['type'], $validated['amount']);
        });

        return redirect()->route('transactions.index')
            ->with('success', 'Transaction created successfully.');
    }

    private function getCurrentBalance($method, $bankId = null, $digitalId = null)
    {
        if ($method === 'cash') {
            $lastTransaction = Transaction::where('user_id', auth()->id())
                ->where('payment_method', 'cash')
                ->orderBy('id', 'desc')
                ->first();
            return $lastTransaction ? $lastTransaction->balance_after : 0;
        }

        if ($method === 'bank' && $bankId) {
            $account = BankAccount::find($bankId);
            return $account ? $account->balance : 0;
        }

        if ($method === 'digital' && $digitalId) {
            $account = DigitalPayment::find($digitalId);
            return $account ? $account->balance : 0;
        }

        return 0;
    }

    private function updateAccountBalance($method, $bankId = null, $digitalId = null, $newBalance)
    {
        if ($method === 'bank' && $bankId) {
            BankAccount::where('id', $bankId)->update(['balance' => $newBalance]);
        }

        if ($method === 'digital' && $digitalId) {
            DigitalPayment::where('id', $digitalId)->update(['balance' => $newBalance]);
        }
    }

    private function updateUserBalance($user, $type, $amount)
    {
        if ($type === 'income') {
            $user->increment('total_balance', $amount);
        } else {
            $user->decrement('total_balance', $amount);
        }
    }

    public function cashIndex(Request $request)
    {
        $query = Transaction::where('payment_method', 'cash')
            ->with(['bankAccount', 'digitalPayment', 'expenseType']);

        if (!auth()->user()->isSuperAdmin()) {
            $query->where('user_id', auth()->id());
        }

        // Filter by date range
        if ($request->filled('from_date')) {
            $query->whereDate('date', '>=', $request->from_date);
        }
        if ($request->filled('to_date')) {
            $query->whereDate('date', '<=', $request->to_date);
        }

        // Filter by type
        if ($request->filled('type')) {
            $query->where('type', $request->type);
        }

        $transactions = $query->orderBy('date', 'desc')
            ->orderBy('id', 'desc')
            ->paginate(20);

        $totalIncome = $query->clone()->where('type', 'income')->sum('amount');
        $totalExpense = $query->clone()->where('type', 'expense')->sum('amount');

        return view('transactions.cash', compact('transactions', 'totalIncome', 'totalExpense'));
    }

public function show(Transaction $transaction)
    {
        $this->authorize('view', $transaction);
        return view('transactions.show', compact('transaction'));
    }

    public function edit(Transaction $transaction)
    {
        $this->authorize('update', $transaction);

        if (auth()->user()->isSuperAdmin()) {
            $bankAccounts = BankAccount::where('is_active', true)->get();
            $digitalPayments = DigitalPayment::where('is_active', true)->get();
            $expenseTypes = ExpenseType::orderBy('name')->get();
        } else {
            $bankAccounts = auth()->user()->bankAccounts()->where('is_active', true)->get();
            $digitalPayments = auth()->user()->digitalPayments()->where('is_active', true)->get();
            $expenseTypes = ExpenseType::where(function ($query) {
                $query->where('user_id', auth()->id())
                    ->orWhere('is_system', true);
            })->orderBy('name')->get();
        }

        return view('transactions.edit', compact('transaction', 'bankAccounts', 'digitalPayments', 'expenseTypes'));
    }

    public function update(Request $request, Transaction $transaction)
    {
        $this->authorize('update', $transaction);

        $validated = $request->validate([
            'date' => 'required|date',
            'type' => 'required|in:income,expense,transfer',
            'payment_method' => 'required|in:cash,bank,digital',
            'bank_account_id' => 'exclude_unless:payment_method,bank|required|exists:bank_accounts,id',
            'digital_payment_id' => 'exclude_unless:payment_method,digital|required|exists:digital_payments,id',
            'expense_type_id' => 'exclude_unless:type,expense|required|exists:expense_types,id',
            'amount' => 'required|numeric|min:0.01',
            'details' => 'nullable|string|max:500',
            'reference' => 'nullable|string|max:100',
            'notes' => 'nullable|string|max:500',
        ]);

        // Get the old amount to recalculate balances
        $oldAmount = $transaction->amount;
        $oldType = $transaction->type;
        $newAmount = $validated['amount'];
        $newType = $validated['type'];

        DB::transaction(function () use ($transaction, $validated, $oldAmount, $oldType, $newAmount, $newType) {
            // Update transaction
            $transaction->update([
                'date' => $validated['date'],
                'type' => $validated['type'],
                'payment_method' => $validated['payment_method'],
                'bank_account_id' => $validated['bank_account_id'] ?? null,
                'digital_payment_id' => $validated['digital_payment_id'] ?? null,
                'expense_type_id' => $validated['expense_type_id'] ?? null,
                'amount' => $validated['amount'],
                'details' => $validated['details'] ?? null,
                'reference' => $validated['reference'] ?? null,
                'notes' => $validated['notes'] ?? null,
            ]);

            // Recalculate balances
            $this->recalculateBalances($transaction, $oldAmount, $oldType, $newAmount, $newType);
        });

        return redirect()->route('transactions.index')
            ->with('success', 'Transaction updated successfully.');
    }

    private function recalculateBalances($transaction, $oldAmount, $oldType, $newAmount, $newType)
    {
        $user = $transaction->user;

        // Reverse the old transaction effect
        if ($oldType === 'income') {
            $user->decrement('total_balance', $oldAmount);
        } else {
            $user->increment('total_balance', $oldAmount);
        }

        // Apply the new transaction effect
        if ($newType === 'income') {
            $user->increment('total_balance', $newAmount);
        } else {
            $user->decrement('total_balance', $newAmount);
        }

        // Recalculate account balances
        $this->recalculateAccountBalance($transaction);
    }

    private function recalculateAccountBalance($transaction)
    {
        $method = $transaction->payment_method;

        if ($method === 'cash') {
            // For cash, recalculate from all cash transactions
            $transactions = Transaction::where('user_id', $transaction->user_id)
                ->where('payment_method', 'cash')
                ->orderBy('date', 'asc')
                ->orderBy('id', 'asc')
                ->get();

            $balance = 0;
            foreach ($transactions as $tx) {
                if ($tx->type === 'income') {
                    $balance += $tx->amount;
                } else {
                    $balance -= $tx->amount;
                }
                $tx->update([
                    'balance_before' => $balance - ($tx->type === 'income' ? $tx->amount : 0),
                    'balance_after' => $balance
                ]);
            }
        } elseif ($method === 'bank' && $transaction->bank_account_id) {
            $bankAccount = BankAccount::find($transaction->bank_account_id);
            if ($bankAccount) {
                $transactions = Transaction::where('user_id', $transaction->user_id)
                    ->where('payment_method', 'bank')
                    ->where('bank_account_id', $transaction->bank_account_id)
                    ->orderBy('date', 'asc')
                    ->orderBy('id', 'asc')
                    ->get();

                $balance = 0;
                foreach ($transactions as $tx) {
                    if ($tx->type === 'income') {
                        $balance += $tx->amount;
                    } else {
                        $balance -= $tx->amount;
                    }
                    $tx->update([
                        'balance_before' => $balance - ($tx->type === 'income' ? $tx->amount : 0),
                        'balance_after' => $balance
                    ]);
                }
                $bankAccount->update(['balance' => $balance]);
            }
        } elseif ($method === 'digital' && $transaction->digital_payment_id) {
            $digitalPayment = DigitalPayment::find($transaction->digital_payment_id);
            if ($digitalPayment) {
                $transactions = Transaction::where('user_id', $transaction->user_id)
                    ->where('payment_method', 'digital')
                    ->where('digital_payment_id', $transaction->digital_payment_id)
                    ->orderBy('date', 'asc')
                    ->orderBy('id', 'asc')
                    ->get();

                $balance = 0;
                foreach ($transactions as $tx) {
                    if ($tx->type === 'income') {
                        $balance += $tx->amount;
                    } else {
                        $balance -= $tx->amount;
                    }
                    $tx->update([
                        'balance_before' => $balance - ($tx->type === 'income' ? $tx->amount : 0),
                        'balance_after' => $balance
                    ]);
                }
                $digitalPayment->update(['balance' => $balance]);
            }
        }
    }

    public function destroy(Transaction $transaction)
    {
        $this->authorize('delete', $transaction);

        $user = $transaction->user;
        $method = $transaction->payment_method;
        $type = $transaction->type;
        $amount = $transaction->amount;

        DB::transaction(function () use ($transaction, $user, $method, $type, $amount) {
            // Reverse the effect on user's total balance
            if ($type === 'income') {
                $user->decrement('total_balance', $amount);
            } else {
                $user->increment('total_balance', $amount);
            }

            // Recalculate account balance after deletion
            $this->recalculateAfterDelete($transaction);

            // Delete the transaction
            $transaction->delete();
        });

        return redirect()->route('transactions.index')
            ->with('success', 'Transaction deleted successfully.');
    }

    private function recalculateAfterDelete($transaction)
    {
        $method = $transaction->payment_method;

        if ($method === 'cash') {
            $transactions = Transaction::where('user_id', $transaction->user_id)
                ->where('payment_method', 'cash')
                ->orderBy('date', 'asc')
                ->orderBy('id', 'asc')
                ->get();

            $balance = 0;
            foreach ($transactions as $tx) {
                if ($tx->type === 'income') {
                    $balance += $tx->amount;
                } else {
                    $balance -= $tx->amount;
                }
                $tx->update([
                    'balance_before' => $balance - ($tx->type === 'income' ? $tx->amount : 0),
                    'balance_after' => $balance
                ]);
            }
        } elseif ($method === 'bank' && $transaction->bank_account_id) {
            $transactions = Transaction::where('user_id', $transaction->user_id)
                ->where('payment_method', 'bank')
                ->where('bank_account_id', $transaction->bank_account_id)
                ->orderBy('date', 'asc')
                ->orderBy('id', 'asc')
                ->get();

            $balance = 0;
            foreach ($transactions as $tx) {
                if ($tx->type === 'income') {
                    $balance += $tx->amount;
                } else {
                    $balance -= $tx->amount;
                }
                $tx->update([
                    'balance_before' => $balance - ($tx->type === 'income' ? $tx->amount : 0),
                    'balance_after' => $balance
                ]);
            }
            BankAccount::where('id', $transaction->bank_account_id)->update(['balance' => $balance]);
        } elseif ($method === 'digital' && $transaction->digital_payment_id) {
            $transactions = Transaction::where('user_id', $transaction->user_id)
                ->where('payment_method', 'digital')
                ->where('digital_payment_id', $transaction->digital_payment_id)
                ->orderBy('date', 'asc')
                ->orderBy('id', 'asc')
                ->get();

            $balance = 0;
            foreach ($transactions as $tx) {
                if ($tx->type === 'income') {
                    $balance += $tx->amount;
                } else {
                    $balance -= $tx->amount;
                }
                $tx->update([
                    'balance_before' => $balance - ($tx->type === 'income' ? $tx->amount : 0),
                    'balance_after' => $balance
                ]);
            }
            DigitalPayment::where('id', $transaction->digital_payment_id)->update(['balance' => $balance]);
        }
    }
}
