<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Transaction;
use App\Models\Account;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;

class TransactionController extends Controller
{
    /**
     * Listar transações com filtros
     */
    public function index(Request $request): JsonResponse
    {
        $query = Transaction::ofUser($request->user()->id)
            ->with(['account:id,name,currency', 'category:id,name,color,icon', 'costCenter:id,name,color']);

        // Filtros
        if ($request->has('account_id')) {
            $query->ofAccount($request->account_id);
        }

        if ($request->has('category_id')) {
            $query->ofCategory($request->category_id);
        }

        if ($request->has('cost_center_id')) {
            $query->ofCostCenter($request->cost_center_id);
        }

        if ($request->has('type')) {
            $query->where('type', $request->type);
        }

        if ($request->has('status')) {
            $query->where('status', $request->status);
        }

        // Filtro por período
        if ($request->has('start_date') && $request->has('end_date')) {
            $dateField = $request->get('date_field', 'planned_date');
            $query->inPeriod($request->start_date, $request->end_date, $dateField);
        }

        // Busca por descrição e valores
        if ($request->has('search')) {
            $search = $request->search;
            // Limpar formatação de número para busca por valor
            $searchNumber = preg_replace('/[^\d.,\-]/', '', $search);
            $searchNumber = str_replace(',', '.', $searchNumber);
            
            $query->where(function($q) use ($search, $searchNumber) {
                $q->where('description', 'like', "%{$search}%")
                  ->orWhere('reference', 'like', "%{$search}%")
                  ->orWhere('notes', 'like', "%{$search}%");
                
                // Se for um número válido, buscar também nos campos de valor
                if (is_numeric($searchNumber)) {
                    $numericValue = (float) $searchNumber;
                    // Busca exata
                    $q->orWhere('planned_amount', '=', $numericValue)
                      ->orWhere('amount', '=', $numericValue);
                    
                    // Busca por valor sem decimais (ex: "147" encontra 147.00, 147.50, etc.)
                    if (strpos($searchNumber, '.') === false) {
                        $q->orWhereBetween('planned_amount', [$numericValue, $numericValue + 0.99])
                          ->orWhereBetween('amount', [$numericValue, $numericValue + 0.99]);
                    }
                }
            });
        }

        // Ordenação
        $sortField = $request->get('sort_by', 'planned_date');
        $sortOrder = $request->get('sort_order', 'desc');
        $query->orderBy($sortField, $sortOrder);

        // Paginação ou todos
        if ($request->has('per_page')) {
            $transactions = $query->paginate($request->per_page);
        } else {
            $transactions = $query->get();
        }

        return response()->json($transactions);
    }

    /**
     * Criar nova transação
     */
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'account_id' => 'required|exists:accounts,id',
            'category_id' => 'nullable|exists:categories,id',
            'cost_center_id' => 'nullable|exists:cost_centers,id',
            'amount' => 'nullable|numeric|min:0',
            'planned_amount' => 'required|numeric|min:0',
            'type' => 'required|in:credit,debit',
            'description' => 'required|string|max:255',
            'notes' => 'nullable|string',
            'effective_date' => 'nullable|date',
            'planned_date' => 'required|date',
            'status' => 'sometimes|in:pending,completed,cancelled',
            'reference' => 'nullable|string|max:100',
            'is_recurring' => 'sometimes|boolean',
        ]);

        // Verificar se a conta pertence ao usuário
        $account = Account::where('id', $validated['account_id'])
            ->where('user_id', $request->user()->id)
            ->firstOrFail();

        $validated['user_id'] = $request->user()->id;
        
        // Se status é completed e não tem amount, usa planned_amount
        if (($validated['status'] ?? 'pending') === 'completed') {
            $validated['amount'] = $validated['amount'] ?? $validated['planned_amount'];
            $validated['effective_date'] = $validated['effective_date'] ?? now()->toDateString();
        }

        $transaction = Transaction::create($validated);

        return response()->json(
            $transaction->load(['account:id,name,currency', 'category:id,name,color,icon', 'costCenter:id,name,color']),
            201
        );
    }

    /**
     * Exibir detalhes de uma transação
     */
    public function show(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar propriedade
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        return response()->json(
            $transaction->load([
                'account:id,name,currency,type',
                'category:id,name,color,icon,parent_id',
                'category.parent:id,name',
                'costCenter:id,name,color',
                'recurringParent:id,description',
                'recurringChildren:id,description,planned_date,status'
            ])
        );
    }

    /**
     * Atualizar transação
     */
    public function update(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar propriedade
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        $validated = $request->validate([
            'account_id' => 'sometimes|exists:accounts,id',
            'category_id' => 'nullable|exists:categories,id',
            'cost_center_id' => 'nullable|exists:cost_centers,id',
            'amount' => 'nullable|numeric|min:0',
            'planned_amount' => 'sometimes|numeric|min:0',
            'type' => 'sometimes|in:credit,debit',
            'description' => 'sometimes|string|max:255',
            'notes' => 'nullable|string',
            'effective_date' => 'nullable|date',
            'planned_date' => 'sometimes|date',
            'status' => 'sometimes|in:pending,completed,cancelled',
            'reference' => 'nullable|string|max:100',
            'is_recurring' => 'sometimes|boolean',
        ]);

        // Se mudou account_id, verificar propriedade
        if (isset($validated['account_id'])) {
            Account::where('id', $validated['account_id'])
                ->where('user_id', $request->user()->id)
                ->firstOrFail();
        }

        // Se mudou para completed, garantir amount e effective_date
        if (isset($validated['status']) && $validated['status'] === 'completed') {
            if (!$transaction->amount && !isset($validated['amount'])) {
                $validated['amount'] = $validated['planned_amount'] ?? $transaction->planned_amount;
            }
            if (!$transaction->effective_date && !isset($validated['effective_date'])) {
                $validated['effective_date'] = now()->toDateString();
            }
        }

        // Se voltou para pending, limpar amount e effective_date
        if (isset($validated['status']) && $validated['status'] === 'pending') {
            $validated['amount'] = null;
            $validated['effective_date'] = null;
        }

        $transaction->update($validated);

        return response()->json(
            $transaction->fresh()->load(['account:id,name,currency', 'category:id,name,color,icon', 'costCenter:id,name,color'])
        );
    }

    /**
     * Excluir transação
     */
    public function destroy(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar propriedade
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        $transaction->delete();

        return response()->json(['message' => 'Transação excluída com sucesso']);
    }

    /**
     * Marcar transação como concluída
     */
    public function complete(Request $request, Transaction $transaction): JsonResponse
    {
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        $validated = $request->validate([
            'amount' => 'nullable|numeric|min:0',
            'effective_date' => 'nullable|date',
        ]);

        $transaction->markAsCompleted(
            $validated['amount'] ?? null,
            $validated['effective_date'] ?? null
        );

        return response()->json(
            $transaction->load(['account:id,name,currency', 'category:id,name,color,icon', 'costCenter:id,name,color'])
        );
    }

    /**
     * Cancelar transação
     */
    public function cancel(Request $request, Transaction $transaction): JsonResponse
    {
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        $transaction->markAsCancelled();

        return response()->json(
            $transaction->load(['account:id,name,currency', 'category:id,name,color,icon', 'costCenter:id,name,color'])
        );
    }

    /**
     * Reverter para pendente
     */
    public function revert(Request $request, Transaction $transaction): JsonResponse
    {
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        $transaction->markAsPending();

        return response()->json(
            $transaction->load(['account:id,name,currency', 'category:id,name,color,icon', 'costCenter:id,name,color'])
        );
    }

    /**
     * Resumo de transações
     */
    public function summary(Request $request): JsonResponse
    {
        $userId = $request->user()->id;
        
        $query = Transaction::ofUser($userId);

        // Filtros opcionais
        if ($request->has('account_id')) {
            $query->ofAccount($request->account_id);
        }

        if ($request->has('start_date') && $request->has('end_date')) {
            $query->inPeriod($request->start_date, $request->end_date);
        }

        // Totais por status
        $byStatus = Transaction::ofUser($userId)
            ->when($request->has('account_id'), fn($q) => $q->ofAccount($request->account_id))
            ->when($request->has('start_date'), fn($q) => $q->inPeriod($request->start_date, $request->end_date))
            ->select('status', DB::raw('COUNT(*) as count'), DB::raw('SUM(COALESCE(amount, planned_amount)) as total'))
            ->groupBy('status')
            ->get()
            ->keyBy('status');

        // Totais por tipo (apenas completed)
        $byType = Transaction::ofUser($userId)
            ->completed()
            ->when($request->has('account_id'), fn($q) => $q->ofAccount($request->account_id))
            ->when($request->has('start_date'), fn($q) => $q->inPeriod($request->start_date, $request->end_date, 'effective_date'))
            ->select('type', DB::raw('COUNT(*) as count'), DB::raw('SUM(amount) as total'))
            ->groupBy('type')
            ->get()
            ->keyBy('type');

        // Transações pendentes atrasadas
        $overdue = Transaction::ofUser($userId)
            ->pending()
            ->when($request->has('account_id'), fn($q) => $q->ofAccount($request->account_id))
            ->where('planned_date', '<', now()->startOfDay())
            ->count();

        return response()->json([
            'by_status' => [
                'pending' => [
                    'count' => $byStatus['pending']->count ?? 0,
                    'total' => (float) ($byStatus['pending']->total ?? 0),
                ],
                'completed' => [
                    'count' => $byStatus['completed']->count ?? 0,
                    'total' => (float) ($byStatus['completed']->total ?? 0),
                ],
                'cancelled' => [
                    'count' => $byStatus['cancelled']->count ?? 0,
                    'total' => (float) ($byStatus['cancelled']->total ?? 0),
                ],
            ],
            'by_type' => [
                'credit' => [
                    'count' => $byType['credit']->count ?? 0,
                    'total' => (float) ($byType['credit']->total ?? 0),
                ],
                'debit' => [
                    'count' => $byType['debit']->count ?? 0,
                    'total' => (float) ($byType['debit']->total ?? 0),
                ],
            ],
            'balance' => (float) (($byType['credit']->total ?? 0) - ($byType['debit']->total ?? 0)),
            'overdue_count' => $overdue,
        ]);
    }

    /**
     * Duplicar transação
     */
    public function duplicate(Request $request, Transaction $transaction): JsonResponse
    {
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        $newTransaction = $transaction->replicate();
        
        // Resetear campos que no deben duplicarse
        $newTransaction->status = 'pending';
        $newTransaction->amount = null;
        $newTransaction->effective_date = null;
        $newTransaction->planned_date = now()->toDateString();
        $newTransaction->import_hash = null;  // IMPORTANTE: debe ser null para evitar duplicidad
        
        $newTransaction->save();

        return response()->json(
            $newTransaction->load(['account:id,name,currency', 'category:id,name,color,icon', 'costCenter:id,name,color']),
            201
        );
    }

    /**
     * Listar transações agrupadas por semana e separadas por divisa
     */
    public function byWeek(Request $request): JsonResponse
    {
        $userId = $request->user()->id;
        $perPage = $request->get('per_page', 10); // Semanas por página
        $page = $request->get('page', 1);
        $currency = $request->get('currency'); // Filtro de divisa opcional
        $dateField = $request->get('date_field', 'planned_date');

        // Buscar IDs de transações que estão conciliadas com passivos
        $reconciledTransactionIds = \App\Models\LiabilityInstallment::whereNotNull('reconciled_transaction_id')
            ->pluck('reconciled_transaction_id')
            ->toArray();

        // Construir query base
        $query = Transaction::ofUser($userId)
            ->with(['account:id,name,currency', 'category:id,name,color,icon', 'costCenter:id,name,color']);

        // Filtros opcionais
        if ($request->has('account_id')) {
            $query->ofAccount($request->account_id);
        }

        if ($request->has('category_id')) {
            $query->ofCategory($request->category_id);
        }

        if ($request->has('cost_center_id')) {
            $query->ofCostCenter($request->cost_center_id);
        }

        if ($request->has('type')) {
            $query->where('type', $request->type);
        }

        if ($request->has('status')) {
            $query->where('status', $request->status);
        }

        // Filtro por período
        if ($request->has('start_date') && $request->has('end_date')) {
            $query->inPeriod($request->start_date, $request->end_date, $dateField);
        }

        // Busca por descrição e valores
        if ($request->has('search')) {
            $search = $request->search;
            // Limpar formatação de número para busca por valor
            $searchNumber = preg_replace('/[^\d.,\-]/', '', $search);
            $searchNumber = str_replace(',', '.', $searchNumber);
            
            $query->where(function($q) use ($search, $searchNumber) {
                $q->where('description', 'like', "%{$search}%")
                  ->orWhere('reference', 'like', "%{$search}%")
                  ->orWhere('notes', 'like', "%{$search}%");
                
                // Se for um número válido, buscar também nos campos de valor
                if (is_numeric($searchNumber)) {
                    $numericValue = (float) $searchNumber;
                    // Busca exata
                    $q->orWhere('planned_amount', '=', $numericValue)
                      ->orWhere('amount', '=', $numericValue);
                    
                    // Busca por valor sem decimais (ex: "147" encontra 147.00, 147.50, etc.)
                    if (strpos($searchNumber, '.') === false) {
                        $q->orWhereBetween('planned_amount', [$numericValue, $numericValue + 0.99])
                          ->orWhereBetween('amount', [$numericValue, $numericValue + 0.99]);
                    }
                }
            });
        }

        // Ordenar por data (effective_date com fallback para planned_date)
        if ($dateField === 'effective_date') {
            $query->orderByRaw('COALESCE(effective_date, planned_date) DESC');
        } else {
            $query->orderBy($dateField, 'desc');
        }

        // Obter todas as transações filtradas
        $allTransactions = $query->get();

        // Agrupar por divisa (da conta)
        $byCurrency = $allTransactions->groupBy(function ($transaction) {
            return $transaction->account->currency ?? 'EUR';
        });

        // Se tem filtro de divisa, aplicar
        if ($currency && $byCurrency->has($currency)) {
            $byCurrency = collect([$currency => $byCurrency->get($currency)]);
        } elseif ($currency) {
            $byCurrency = collect(); // Divisa não encontrada
        }

        // Estrutura de resultado por divisa
        $result = [];
        
        foreach ($byCurrency as $currencyCode => $currencyTransactions) {
            // Agrupar transações por semana (YEARWEEK)
            $byWeek = $currencyTransactions->groupBy(function ($transaction) use ($dateField) {
                // Usar effective_date com fallback para planned_date
                $date = $dateField === 'effective_date' 
                    ? ($transaction->effective_date ?? $transaction->planned_date)
                    : $transaction->$dateField;
                $carbon = \Carbon\Carbon::parse($date);
                // Usar ISO week (segunda a domingo)
                return $carbon->format('o-W'); // ISO year-week (ex: 2025-49)
            });

            // Ordenar semanas (mais recentes primeiro)
            $byWeek = $byWeek->sortKeysDesc();

            // Calcular total de semanas para paginação
            $totalWeeks = $byWeek->count();
            
            // Aplicar paginação manual (semanas)
            $paginatedWeeks = $byWeek->skip(($page - 1) * $perPage)->take($perPage);

            $weeks = [];
            foreach ($paginatedWeeks as $yearWeek => $weekTransactions) {
                // Calcular datas de início e fim da semana
                [$year, $week] = explode('-', $yearWeek);
                $startOfWeek = \Carbon\Carbon::now()
                    ->setISODate((int)$year, (int)$week, 1) // Segunda-feira
                    ->startOfDay();
                $endOfWeek = $startOfWeek->copy()->addDays(6)->endOfDay();

                // Separar transferências das transações normais
                $normalTransactions = $weekTransactions->filter(fn($t) => !$t->is_transfer);
                $transfers = $weekTransactions->filter(fn($t) => $t->is_transfer);

                // Ordenar transações dentro da semana por data (effective_date com fallback para planned_date)
                $normalTransactions = $normalTransactions->sortByDesc(function ($t) use ($dateField) {
                    if ($dateField === 'effective_date') {
                        return $t->effective_date ?? $t->planned_date;
                    }
                    return $t->$dateField;
                });

                // Calcular resumo financeiro da semana (excluindo transferências)
                $credits = $normalTransactions->where('type', 'credit');
                $debits = $normalTransactions->where('type', 'debit');
                $pending = $normalTransactions->where('status', 'pending');
                $completed = $normalTransactions->where('status', 'completed');
                $overdue = $normalTransactions->where('status', 'pending')
                    ->filter(function ($t) {
                        return \Carbon\Carbon::parse($t->planned_date)->lt(now()->startOfDay());
                    });

                $totalCredits = $credits->sum(function ($t) {
                    if ($t->status === 'completed') {
                        return $t->amount > 0 ? $t->amount : $t->planned_amount;
                    }
                    return $t->planned_amount;
                });
                
                $totalDebits = $debits->sum(function ($t) {
                    if ($t->status === 'completed') {
                        return $t->amount > 0 ? $t->amount : $t->planned_amount;
                    }
                    return $t->planned_amount;
                });

                // Agrupar transferências em pares (débito → crédito)
                $processedTransferIds = [];
                $groupedTransfers = [];
                
                foreach ($transfers as $transfer) {
                    // Pular se já processamos esta transferência
                    if (in_array($transfer->id, $processedTransferIds)) {
                        continue;
                    }
                    
                    $linkedTransfer = null;
                    if ($transfer->transfer_linked_id) {
                        $linkedTransfer = $transfers->firstWhere('id', $transfer->transfer_linked_id);
                    }
                    
                    // Determinar qual é o débito e qual é o crédito
                    $debitTransaction = $transfer->type === 'debit' ? $transfer : $linkedTransfer;
                    $creditTransaction = $transfer->type === 'credit' ? $transfer : $linkedTransfer;
                    
                    // Se não encontrou o par, usar apenas esta transação
                    if (!$linkedTransfer) {
                        $debitTransaction = $transfer->type === 'debit' ? $transfer : null;
                        $creditTransaction = $transfer->type === 'credit' ? $transfer : null;
                    }
                    
                    $amount = $debitTransaction 
                        ? ($debitTransaction->status === 'completed' ? $debitTransaction->amount : $debitTransaction->planned_amount)
                        : ($creditTransaction->status === 'completed' ? $creditTransaction->amount : $creditTransaction->planned_amount);
                    
                    $groupedTransfers[] = [
                        'id' => $transfer->id,
                        'is_transfer_pair' => true,
                        'description' => $transfer->description,
                        'original_description' => $transfer->original_description,
                        'amount' => (float) $amount,
                        'status' => $transfer->status,
                        'planned_date' => $transfer->planned_date?->format('Y-m-d'),
                        'effective_date' => $transfer->effective_date?->format('Y-m-d'),
                        'from_account' => $debitTransaction && $debitTransaction->account ? [
                            'id' => $debitTransaction->account->id,
                            'name' => $debitTransaction->account->name,
                            'currency' => $debitTransaction->account->currency,
                        ] : null,
                        'to_account' => $creditTransaction && $creditTransaction->account ? [
                            'id' => $creditTransaction->account->id,
                            'name' => $creditTransaction->account->name,
                            'currency' => $creditTransaction->account->currency,
                        ] : null,
                        'debit_transaction_id' => $debitTransaction?->id,
                        'credit_transaction_id' => $creditTransaction?->id,
                    ];
                    
                    // Marcar ambas as transações como processadas
                    $processedTransferIds[] = $transfer->id;
                    if ($linkedTransfer) {
                        $processedTransferIds[] = $linkedTransfer->id;
                    }
                }

                $weeks[] = [
                    'year_week' => $yearWeek,
                    'year' => (int)$year,
                    'week_number' => (int)$week,
                    'start_date' => $startOfWeek->format('Y-m-d'),
                    'end_date' => $endOfWeek->format('Y-m-d'),
                    'summary' => [
                        'total_transactions' => $normalTransactions->count(),
                        'transfers_count' => count($groupedTransfers),
                        'credits' => [
                            'count' => $credits->count(),
                            'total' => (float) $totalCredits,
                        ],
                        'debits' => [
                            'count' => $debits->count(),
                            'total' => (float) $totalDebits,
                        ],
                        'balance' => (float) ($totalCredits - $totalDebits),
                        'pending' => [
                            'count' => $pending->count(),
                            'total' => (float) $pending->sum('planned_amount'),
                        ],
                        'completed' => [
                            'count' => $completed->count(),
                            'total' => (float) $completed->sum(fn($t) => $t->amount > 0 ? $t->amount : $t->planned_amount),
                        ],
                        'overdue_count' => $overdue->count(),
                    ],
                    'transactions' => $normalTransactions->values()->map(function ($t) use ($reconciledTransactionIds) {
                        return [
                            'id' => $t->id,
                            'description' => $t->description,
                            'original_description' => $t->original_description,
                            'type' => $t->type,
                            'status' => $t->status,
                            'amount' => (float) $t->amount,
                            'planned_amount' => (float) $t->planned_amount,
                            'planned_date' => $t->planned_date?->format('Y-m-d'),
                            'effective_date' => $t->effective_date?->format('Y-m-d'),
                            'reference' => $t->reference,
                            'notes' => $t->notes,
                            'is_transfer' => false,
                            'is_reconciled' => in_array($t->id, $reconciledTransactionIds),
                            'account' => $t->account ? [
                                'id' => $t->account->id,
                                'name' => $t->account->name,
                                'currency' => $t->account->currency,
                            ] : null,
                            'category' => $t->category ? [
                                'id' => $t->category->id,
                                'name' => $t->category->name,
                                'color' => $t->category->color,
                                'icon' => $t->category->icon,
                            ] : null,
                            'cost_center' => $t->costCenter ? [
                                'id' => $t->costCenter->id,
                                'name' => $t->costCenter->name,
                                'color' => $t->costCenter->color,
                            ] : null,
                            'is_overdue' => $t->status === 'pending' && 
                                \Carbon\Carbon::parse($t->planned_date)->lt(now()->startOfDay()),
                        ];
                    }),
                    'transfers' => $groupedTransfers,
                ];
            }

            $result[$currencyCode] = [
                'currency' => $currencyCode,
                'total_transactions' => $currencyTransactions->count(),
                'pagination' => [
                    'current_page' => (int) $page,
                    'per_page' => (int) $perPage,
                    'total_weeks' => $totalWeeks,
                    'total_pages' => (int) ceil($totalWeeks / $perPage),
                    'has_more' => $page < ceil($totalWeeks / $perPage),
                ],
                'weeks' => $weeks,
            ];
        }

        // Retornar divisas disponíveis
        $availableCurrencies = array_keys($result);

        return response()->json([
            'currencies' => $availableCurrencies,
            'selected_currency' => $currency,
            'data' => $result,
        ]);
    }

    /**
     * Criar transferência entre contas
     * Cria duas transações vinculadas: débito na origem, crédito no destino
     */
    public function transfer(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'from_account_id' => 'required|exists:accounts,id',
            'to_account_id' => 'required|exists:accounts,id|different:from_account_id',
            'amount' => 'required|numeric|min:0.01',
            'description' => 'nullable|string|max:255',
            'date' => 'required|date',
            'notes' => 'nullable|string',
        ]);

        $userId = $request->user()->id;

        // Verificar se ambas as contas pertencem ao usuário
        $fromAccount = Account::where('id', $validated['from_account_id'])
            ->where('user_id', $userId)
            ->firstOrFail();

        $toAccount = Account::where('id', $validated['to_account_id'])
            ->where('user_id', $userId)
            ->firstOrFail();

        $description = $validated['description'] ?? "Transferência: {$fromAccount->name} → {$toAccount->name}";

        DB::beginTransaction();
        try {
            // Criar transação de DÉBITO na conta de origem
            $debitTransaction = Transaction::create([
                'user_id' => $userId,
                'account_id' => $fromAccount->id,
                'type' => 'debit',
                'amount' => $validated['amount'],
                'planned_amount' => $validated['amount'],
                'description' => $description,
                'effective_date' => $validated['date'],
                'planned_date' => $validated['date'],
                'status' => 'completed',
                'notes' => $validated['notes'] ?? "Transferência para {$toAccount->name}",
                'reference' => 'TRANSFER',
            ]);

            // Criar transação de CRÉDITO na conta de destino
            $creditTransaction = Transaction::create([
                'user_id' => $userId,
                'account_id' => $toAccount->id,
                'type' => 'credit',
                'amount' => $validated['amount'],
                'planned_amount' => $validated['amount'],
                'description' => $description,
                'effective_date' => $validated['date'],
                'planned_date' => $validated['date'],
                'status' => 'completed',
                'notes' => $validated['notes'] ?? "Transferência de {$fromAccount->name}",
                'reference' => 'TRANSFER',
            ]);

            // Vincular as duas transações
            $debitTransaction->update(['transfer_pair_id' => $creditTransaction->id]);
            $creditTransaction->update(['transfer_pair_id' => $debitTransaction->id]);

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Transferência realizada com sucesso',
                'data' => [
                    'debit' => $debitTransaction->load('account'),
                    'credit' => $creditTransaction->load('account'),
                ],
            ], 201);

        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Erro ao realizar transferência: ' . $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Desvincular transferência
     * Remove os flags is_transfer e transfer_linked_id de ambas as transações
     */
    public function unlinkTransfer(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar se pertence ao usuário
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        // Verificar se é uma transferência
        if (!$transaction->is_transfer) {
            return response()->json([
                'success' => false,
                'message' => 'Esta transação não é uma transferência',
            ], 422);
        }

        DB::beginTransaction();
        try {
            // Encontrar a transação vinculada (se existir)
            $linkedTransaction = null;
            if ($transaction->transfer_linked_id) {
                $linkedTransaction = Transaction::where('id', $transaction->transfer_linked_id)
                    ->where('user_id', $request->user()->id)
                    ->first();
            }
            
            // Também verificar pelo transfer_pair_id
            if (!$linkedTransaction && $transaction->transfer_pair_id) {
                $linkedTransaction = Transaction::where('id', $transaction->transfer_pair_id)
                    ->where('user_id', $request->user()->id)
                    ->first();
            }

            // Remover flags da transação principal
            $transaction->update([
                'is_transfer' => false,
                'transfer_linked_id' => null,
                'transfer_pair_id' => null,
                'reference' => $transaction->reference === 'TRANSFER' ? null : $transaction->reference,
            ]);

            // Remover flags da transação vinculada (se existir)
            if ($linkedTransaction) {
                $linkedTransaction->update([
                    'is_transfer' => false,
                    'transfer_linked_id' => null,
                    'transfer_pair_id' => null,
                    'reference' => $linkedTransaction->reference === 'TRANSFER' ? null : $linkedTransaction->reference,
                ]);
            }

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Transferência desvinculada com sucesso',
                'data' => [
                    'transaction' => $transaction->fresh()->load(['account', 'category', 'costCenter']),
                    'linked_transaction' => $linkedTransaction?->fresh()->load(['account', 'category', 'costCenter']),
                ],
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Erro ao desvincular transferência: ' . $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Efetivação rápida de transação pendente
     * Permite efetivar direto da listagem com dados mínimos
     */
    public function quickComplete(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar se pertence ao usuário
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        if ($transaction->status !== 'pending') {
            return response()->json([
                'success' => false,
                'message' => 'Apenas transações pendentes podem ser efetivadas',
            ], 422);
        }

        $validated = $request->validate([
            'amount' => 'nullable|numeric|min:0',
            'effective_date' => 'nullable|date',
        ]);

        $transaction->update([
            'status' => 'completed',
            'amount' => $validated['amount'] ?? $transaction->planned_amount,
            'effective_date' => $validated['effective_date'] ?? now()->toDateString(),
        ]);

        return response()->json([
            'success' => true,
            'message' => 'Transação efetivada com sucesso',
            'data' => $transaction->load(['account', 'category', 'costCenter']),
        ]);
    }

    /**
     * Dividir transação em múltiplas categorias
     * A transação original é marcada como "pai" e novas transações filhas são criadas
     */
    public function split(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar se pertence ao usuário
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        // Não pode dividir transação já dividida ou filha
        if ($transaction->is_split_parent || $transaction->is_split_child) {
            return response()->json([
                'success' => false,
                'message' => 'Esta transação já foi dividida ou é resultado de uma divisão',
            ], 422);
        }

        $validated = $request->validate([
            'splits' => 'required|array|min:2',
            'splits.*.category_id' => 'nullable|exists:categories,id',
            'splits.*.amount' => 'required|numeric|min:0.01',
            'splits.*.description' => 'nullable|string|max:255',
        ]);

        // Validar que a soma das divisões é igual ao valor original
        $totalSplit = collect($validated['splits'])->sum('amount');
        $originalAmount = $transaction->amount ?? $transaction->planned_amount;

        if (abs($totalSplit - $originalAmount) > 0.01) {
            return response()->json([
                'success' => false,
                'message' => "A soma das divisões ({$totalSplit}) deve ser igual ao valor original ({$originalAmount})",
            ], 422);
        }

        DB::beginTransaction();
        try {
            // Marcar transação original como pai
            $transaction->update([
                'is_split_parent' => true,
            ]);

            $splitTransactions = [];

            foreach ($validated['splits'] as $index => $split) {
                $splitTransaction = Transaction::create([
                    'user_id' => $transaction->user_id,
                    'account_id' => $transaction->account_id,
                    'category_id' => $split['category_id'] ?? null,
                    'cost_center_id' => $transaction->cost_center_id,
                    'type' => $transaction->type,
                    'amount' => $transaction->status === 'completed' ? $split['amount'] : null,
                    'planned_amount' => $split['amount'],
                    'description' => $split['description'] ?? "{$transaction->description} (Parte " . ($index + 1) . ")",
                    'original_description' => $transaction->original_description,
                    'effective_date' => $transaction->effective_date,
                    'planned_date' => $transaction->planned_date,
                    'status' => $transaction->status,
                    'notes' => $transaction->notes,
                    'reference' => $transaction->reference,
                    'parent_transaction_id' => $transaction->id,
                    'is_split_child' => true,
                ]);

                $splitTransactions[] = $splitTransaction;
            }

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Transação dividida com sucesso em ' . count($splitTransactions) . ' partes',
                'data' => [
                    'parent' => $transaction->fresh(['account', 'category', 'costCenter']),
                    'splits' => collect($splitTransactions)->map(fn($t) => $t->load(['account', 'category', 'costCenter'])),
                ],
            ], 201);

        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Erro ao dividir transação: ' . $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Desfazer divisão de transação
     * Remove as transações filhas e restaura a transação pai
     */
    public function unsplit(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar se pertence ao usuário
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        if (!$transaction->is_split_parent) {
            return response()->json([
                'success' => false,
                'message' => 'Esta transação não foi dividida',
            ], 422);
        }

        DB::beginTransaction();
        try {
            // Deletar transações filhas
            Transaction::where('parent_transaction_id', $transaction->id)->delete();

            // Restaurar transação pai
            $transaction->update([
                'is_split_parent' => false,
            ]);

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Divisão desfeita com sucesso',
                'data' => $transaction->fresh(['account', 'category', 'costCenter']),
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Erro ao desfazer divisão: ' . $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Obter transações filhas de uma divisão
     */
    public function getSplits(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar se pertence ao usuário
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        if (!$transaction->is_split_parent) {
            return response()->json([
                'success' => false,
                'message' => 'Esta transação não foi dividida',
            ], 422);
        }

        $splits = Transaction::where('parent_transaction_id', $transaction->id)
            ->with(['category', 'costCenter'])
            ->get();

        return response()->json([
            'success' => true,
            'data' => [
                'parent' => $transaction->load(['account', 'category', 'costCenter']),
                'splits' => $splits,
            ],
        ]);
    }

    /**
     * Buscar parcelas de passivo compatíveis para conciliação
     */
    public function findLiabilityInstallments(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar se pertence ao usuário
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        // Só transações de débito podem ser conciliadas com passivos
        if ($transaction->type !== 'debit') {
            return response()->json([
                'success' => false,
                'message' => 'Apenas transações de débito podem ser conciliadas com passivos',
            ], 422);
        }

        // Já está conciliada?
        $alreadyReconciled = \App\Models\LiabilityInstallment::where('reconciled_transaction_id', $transaction->id)->exists();
        if ($alreadyReconciled) {
            return response()->json([
                'success' => false,
                'message' => 'Esta transação já está conciliada com um passivo',
            ], 422);
        }

        $toleranceDays = $request->input('tolerance_days', 15);
        $toleranceAmount = $request->input('tolerance_amount', 0.10); // 10% de tolerância no valor

        // Considera tanto o valor efetivo quanto o planejado da transação
        $transactionEffective = abs($transaction->amount ?: 0);
        $transactionPlanned = abs($transaction->planned_amount ?: 0);
        // Ambas as datas para matching
        $transactionDateEffective = $transaction->effective_date;
        $transactionDatePlanned = $transaction->planned_date;
        $transactionDate = $transactionDateEffective ?: $transactionDatePlanned;

        // Buscar parcelas pendentes com valores e datas próximas
        $installments = \App\Models\LiabilityInstallment::whereNull('reconciled_transaction_id')
            ->whereIn('status', ['pending', 'overdue'])
            ->whereHas('liabilityAccount', function ($q) use ($request) {
                $q->where('user_id', $request->user()->id);
            })
            ->with(['liabilityAccount:id,name,creditor'])
            ->get()
            ->map(function ($installment) use ($transactionEffective, $transactionPlanned, $transactionDateEffective, $transactionDatePlanned, $toleranceDays, $toleranceAmount) {
                // Valores da parcela: planejado e pago
                $installmentPlanned = abs($installment->installment_amount);
                $installmentPaid = $installment->paid_amount ? abs($installment->paid_amount) : null;
                
                // Calcular todas as combinações possíveis de diferença
                $combinations = [];
                
                // TX efetivo vs Parcela planejado
                if ($transactionEffective > 0) {
                    $diff = abs($transactionEffective - $installmentPlanned);
                    $pct = $installmentPlanned > 0 ? ($diff / $installmentPlanned) : 1;
                    $combinations[] = ['diff' => $diff, 'pct' => $pct, 'type' => 'effective_vs_planned'];
                }
                
                // TX planejado vs Parcela planejado
                if ($transactionPlanned > 0) {
                    $diff = abs($transactionPlanned - $installmentPlanned);
                    $pct = $installmentPlanned > 0 ? ($diff / $installmentPlanned) : 1;
                    $combinations[] = ['diff' => $diff, 'pct' => $pct, 'type' => 'planned_vs_planned'];
                }
                
                // TX efetivo vs Parcela pago
                if ($transactionEffective > 0 && $installmentPaid !== null && $installmentPaid > 0) {
                    $diff = abs($transactionEffective - $installmentPaid);
                    $pct = $installmentPaid > 0 ? ($diff / $installmentPaid) : 1;
                    $combinations[] = ['diff' => $diff, 'pct' => $pct, 'type' => 'effective_vs_paid'];
                }
                
                // Escolher a melhor combinação (menor diferença percentual)
                usort($combinations, fn($a, $b) => $a['pct'] <=> $b['pct']);
                $best = $combinations[0] ?? null;
                
                if (!$best) return null;
                
                $amountDiff = $best['diff'];
                $amountDiffPercent = $best['pct'];
                $matchType = $best['type'];
                
                // Calcular confiança
                $confidence = 0;
                $reasons = [];

                // Valor exato ou muito próximo
                if ($amountDiff < 0.01) {
                    $confidence += 50;
                    $reasons[] = 'exact_amount';
                    $reasons[] = $matchType;
                } elseif ($amountDiffPercent <= $toleranceAmount) {
                    $confidence += 30;
                    $reasons[] = 'similar_amount';
                    $reasons[] = $matchType;
                } else {
                    return null; // Valor muito diferente, ignorar
                }

                // Calcular diferença de datas considerando ambas as datas da transação
                $daysDiffEffective = $transactionDateEffective ? abs($transactionDateEffective->diffInDays($installment->due_date)) : PHP_INT_MAX;
                $daysDiffPlanned = $transactionDatePlanned ? abs($transactionDatePlanned->diffInDays($installment->due_date)) : PHP_INT_MAX;
                $daysDiff = min($daysDiffEffective, $daysDiffPlanned);

                // Data próxima
                if ($daysDiff == 0) {
                    $confidence += 30;
                    $reasons[] = 'same_date';
                } elseif ($daysDiff <= 3) {
                    $confidence += 25;
                    $reasons[] = 'within_3_days';
                } elseif ($daysDiff <= 7) {
                    $confidence += 15;
                    $reasons[] = 'within_7_days';
                } elseif ($daysDiff <= $toleranceDays) {
                    $confidence += 5;
                    $reasons[] = 'within_tolerance';
                } else {
                    return null; // Data muito diferente, ignorar
                }

                // Status overdue aumenta chance
                if ($installment->status === 'overdue') {
                    $confidence += 10;
                    $reasons[] = 'overdue';
                }

                $level = $confidence >= 70 ? 'high' : ($confidence >= 50 ? 'medium' : 'low');
                
                // Calcular sobrepagamento (cargo/juros extra)
                $transactionTotalAmount = $transactionEffective > 0 ? $transactionEffective : $transactionPlanned;
                $overpayment = $transactionTotalAmount - $installmentPlanned;
                $hasOverpayment = $overpayment > 0.01;

                return [
                    'id' => $installment->id,
                    'liability_account_id' => $installment->liability_account_id,
                    'liability_name' => $installment->liabilityAccount->name ?? 'N/A',
                    'creditor' => $installment->liabilityAccount->creditor ?? null,
                    'installment_number' => $installment->installment_number,
                    'due_date' => $installment->due_date->format('Y-m-d'),
                    'installment_amount' => (float) $installment->installment_amount,
                    'status' => $installment->status,
                    'days_diff' => $daysDiff,
                    'amount_diff' => round($amountDiff, 2),
                    'overpayment' => $hasOverpayment ? round($overpayment, 2) : null,
                    'has_overpayment' => $hasOverpayment,
                    'confidence' => [
                        'percentage' => min(100, $confidence),
                        'level' => $level,
                        'reasons' => $reasons,
                    ],
                ];
            })
            ->filter()
            ->sortByDesc(fn($i) => $i['confidence']['percentage'])
            ->values();

        return response()->json([
            'success' => true,
            'transaction' => [
                'id' => $transaction->id,
                'description' => $transaction->description,
                'amount' => (float) abs($transaction->amount ?: $transaction->planned_amount),
                'date' => $transactionDate->format('Y-m-d'),
            ],
            'installments' => $installments,
            'total' => $installments->count(),
        ]);
    }

    /**
     * Conciliar transação com uma parcela de passivo
     */
    public function reconcileWithLiability(Request $request, Transaction $transaction): JsonResponse
    {
        // Verificar se pertence ao usuário
        if ($transaction->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Transação não encontrada'], 404);
        }

        $validated = $request->validate([
            'installment_id' => 'required|integer|exists:liability_installments,id',
            'fee_amount' => 'nullable|numeric|min:0', // Cargo/juros extra (sobrepagamento)
        ]);

        $installment = \App\Models\LiabilityInstallment::with('liabilityAccount')
            ->findOrFail($validated['installment_id']);

        // Verificar se a parcela pertence ao usuário
        if ($installment->liabilityAccount->user_id !== $request->user()->id) {
            return response()->json(['message' => 'Parcela não encontrada'], 404);
        }

        // Verificar se a parcela já está conciliada
        if ($installment->reconciled_transaction_id) {
            return response()->json([
                'success' => false,
                'message' => 'Esta parcela já está conciliada com outra transação',
            ], 422);
        }

        try {
            DB::beginTransaction();

            $paidAmount = abs($transaction->amount ?: $transaction->planned_amount);
            $plannedAmount = (float) $installment->installment_amount;
            $feeAmount = $validated['fee_amount'] ?? 0;
            
            // Se há sobrepagamento e não foi especificado fee, calcular automaticamente
            if ($paidAmount > $plannedAmount && $feeAmount == 0) {
                $feeAmount = $paidAmount - $plannedAmount;
            }

            // Atualizar parcela
            $installment->reconciled_transaction_id = $transaction->id;
            $installment->payment_account_id = $transaction->account_id;
            $installment->status = \App\Models\LiabilityInstallment::STATUS_PAID;
            $installment->paid_amount = $paidAmount;
            $installment->paid_date = $transaction->effective_date ?: $transaction->planned_date;
            
            // Se há cargo/juros extra, registrar no fee_amount
            if ($feeAmount > 0) {
                $installment->fee_amount = ($installment->fee_amount ?? 0) + $feeAmount;
            }
            
            $installment->save();

            // Recalcular totais da conta passivo
            $installment->liabilityAccount->recalculateTotals();

            DB::commit();
            
            $responseData = [
                'transaction_id' => $transaction->id,
                'installment_id' => $installment->id,
                'liability_name' => $installment->liabilityAccount->name,
                'paid_amount' => $paidAmount,
                'planned_amount' => $plannedAmount,
            ];
            
            if ($feeAmount > 0) {
                $responseData['fee_registered'] = $feeAmount;
                $responseData['message_detail'] = "Sobrepagamento de {$feeAmount} registrado como cargo/juros";
            }

            return response()->json([
                'success' => true,
                'message' => 'Transação conciliada com sucesso',
                'data' => $responseData,
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false,
                'message' => 'Erro ao conciliar: ' . $e->getMessage(),
            ], 500);
        }
    }
}
