<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\LiabilityAccount;
use App\Models\LiabilityInstallment;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use PhpOffice\PhpSpreadsheet\IOFactory;

class LiabilityAccountController extends Controller
{
    /**
     * Listar todas as contas passivo do usuário
     */
    public function index(Request $request): JsonResponse
    {
        $query = LiabilityAccount::where('user_id', Auth::id())
            ->with(['installments' => function ($q) {
                $q->orderBy('installment_number');
            }]);

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

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

        $accounts = $query->orderBy('name')->get();

        // Calcular resumo
        $summary = [
            'total_principal' => $accounts->sum('principal_amount'),
            'total_paid' => $accounts->sum('total_paid'),
            'total_pending' => $accounts->sum('total_pending'),
            'total_interest' => $accounts->sum('total_interest'),
            'total_fees' => $accounts->sum('total_fees'),
            'contracts_count' => $accounts->count(),
            'active_contracts' => $accounts->where('status', 'active')->count(),
        ];

        return response()->json([
            'success' => true,
            'data' => $accounts,
            'summary' => $summary,
            'statuses' => LiabilityAccount::STATUSES,
        ]);
    }

    /**
     * Criar nova conta passivo manualmente
     */
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'name' => 'required|string|max:150',
            'contract_number' => 'nullable|string|max:100',
            'creditor' => 'nullable|string|max:150',
            'description' => 'nullable|string',
            'principal_amount' => 'required|numeric|min:0',
            'currency' => 'nullable|string|size:3',
            'color' => 'nullable|string|max:7',
            'icon' => 'nullable|string|max:50',
            'start_date' => 'nullable|date',
        ]);

        $validated['user_id'] = Auth::id();
        $validated['total_pending'] = $validated['principal_amount'];
        $validated['status'] = LiabilityAccount::STATUS_ACTIVE;

        $account = LiabilityAccount::create($validated);

        return response()->json([
            'success' => true,
            'message' => 'Conta passivo criada com sucesso',
            'data' => $account,
        ], 201);
    }

    /**
     * Exibir uma conta passivo específica com todas as parcelas
     */
    public function show(int $id): JsonResponse
    {
        $account = LiabilityAccount::where('user_id', Auth::id())
            ->with(['installments' => function ($q) {
                $q->orderBy('installment_number');
            }])
            ->findOrFail($id);

        return response()->json([
            'success' => true,
            'data' => $account,
        ]);
    }

    /**
     * Atualizar uma conta passivo
     */
    public function update(Request $request, int $id): JsonResponse
    {
        $account = LiabilityAccount::where('user_id', Auth::id())->findOrFail($id);

        $validated = $request->validate([
            'name' => 'sometimes|required|string|max:150',
            'contract_number' => 'nullable|string|max:100',
            'creditor' => 'nullable|string|max:150',
            'description' => 'nullable|string',
            'currency' => 'nullable|string|size:3',
            'color' => 'nullable|string|max:7',
            'icon' => 'nullable|string|max:50',
            'status' => ['sometimes', Rule::in(array_keys(LiabilityAccount::STATUSES))],
            'is_active' => 'nullable|boolean',
        ]);

        $account->update($validated);

        return response()->json([
            'success' => true,
            'message' => 'Conta passivo atualizada com sucesso',
            'data' => $account->fresh(),
        ]);
    }

    /**
     * Excluir uma conta passivo
     */
    public function destroy(int $id): JsonResponse
    {
        $account = LiabilityAccount::where('user_id', Auth::id())->findOrFail($id);
        $account->delete();

        return response()->json([
            'success' => true,
            'message' => 'Conta passivo excluída com sucesso',
        ]);
    }

    /**
     * Importar contrato de arquivo Excel
     */
    public function import(Request $request): JsonResponse
    {
        $request->validate([
            'file' => 'required|file|mimes:xlsx,xls',
            'name' => 'required|string|max:150',
            'creditor' => 'nullable|string|max:150',
            'contract_number' => 'nullable|string|max:100',
            'currency' => 'nullable|string|size:3',
            'description' => 'nullable|string',
        ]);

        try {
            DB::beginTransaction();

            // Criar conta passivo
            $liabilityAccount = LiabilityAccount::create([
                'user_id' => Auth::id(),
                'name' => $request->name,
                'creditor' => $request->creditor,
                'contract_number' => $request->contract_number,
                'currency' => $request->currency ?? 'EUR',
                'description' => $request->description,
                'principal_amount' => 0, // Será calculado
                'status' => LiabilityAccount::STATUS_ACTIVE,
            ]);

            // Processar arquivo Excel
            $file = $request->file('file');
            $spreadsheet = IOFactory::load($file->getPathname());
            $worksheet = $spreadsheet->getActiveSheet();
            $rows = $worksheet->toArray();

            // Pular cabeçalho
            $header = array_shift($rows);
            
            // Mapear colunas (baseado no formato do arquivo exemplo)
            // Colunas: Pago, Fecha, Cuota, Intereses, Capital, Estado
            $columnMap = $this->mapColumns($header);

            $installments = [];
            foreach ($rows as $row) {
                if (empty($row[$columnMap['installment_number']])) {
                    continue;
                }

                $installmentNumber = (int) $row[$columnMap['installment_number']];
                $dueDate = $this->parseDate($row[$columnMap['due_date']]);
                $installmentAmount = $this->parseAmount($row[$columnMap['installment_amount']]);
                $interestAmount = $this->parseAmount($row[$columnMap['interest_amount']]);
                $principalAmount = $this->parseAmount($row[$columnMap['principal_amount']]);
                $status = $this->parseStatus($row[$columnMap['status']]);

                // Calcular taxa extra (se cuota > capital + juros)
                $normalAmount = $principalAmount + $interestAmount;
                $feeAmount = max(0, $installmentAmount - $normalAmount);

                $installments[] = [
                    'liability_account_id' => $liabilityAccount->id,
                    'installment_number' => $installmentNumber,
                    'due_date' => $dueDate,
                    'installment_amount' => $installmentAmount,
                    'principal_amount' => $principalAmount,
                    'interest_amount' => $interestAmount,
                    'fee_amount' => $feeAmount,
                    'status' => $status,
                    'paid_amount' => $status === 'paid' ? $installmentAmount : 0,
                    'paid_date' => $status === 'paid' ? $dueDate : null,
                    'created_at' => now(),
                    'updated_at' => now(),
                ];
            }

            // Inserir parcelas
            LiabilityInstallment::insert($installments);

            // Recalcular totais
            $liabilityAccount->recalculateTotals();

            DB::commit();

            // Recarregar com parcelas
            $liabilityAccount = LiabilityAccount::with('installments')->find($liabilityAccount->id);

            return response()->json([
                'success' => true,
                'message' => 'Contrato importado com sucesso',
                'data' => $liabilityAccount,
                'imported_installments' => count($installments),
            ], 201);

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

    /**
     * Mapear colunas do Excel para campos do sistema
     */
    private function mapColumns(array $header): array
    {
        $map = [
            'installment_number' => 0, // Pago (número da parcela)
            'due_date' => 1, // Fecha
            'installment_amount' => 2, // Cuota
            'interest_amount' => 3, // Intereses
            'principal_amount' => 4, // Capital
            'status' => 5, // Estado
        ];

        // Tentar mapear automaticamente baseado nos nomes das colunas
        foreach ($header as $index => $columnName) {
            $columnName = strtolower(trim($columnName));
            
            if (in_array($columnName, ['pago', 'numero', 'nº', 'n', 'parcela', 'installment'])) {
                $map['installment_number'] = $index;
            } elseif (in_array($columnName, ['fecha', 'date', 'data', 'vencimiento', 'due_date'])) {
                $map['due_date'] = $index;
            } elseif (in_array($columnName, ['cuota', 'quota', 'valor', 'amount', 'installment_amount'])) {
                $map['installment_amount'] = $index;
            } elseif (in_array($columnName, ['intereses', 'interest', 'juros'])) {
                $map['interest_amount'] = $index;
            } elseif (in_array($columnName, ['capital', 'principal', 'amortización', 'amortizacion'])) {
                $map['principal_amount'] = $index;
            } elseif (in_array($columnName, ['estado', 'status', 'situação', 'situacion'])) {
                $map['status'] = $index;
            }
        }

        return $map;
    }

    /**
     * Converter string de data para formato válido
     */
    private function parseDate($value): string
    {
        if ($value instanceof \DateTime) {
            return $value->format('Y-m-d');
        }

        // Se for número (Excel serial date)
        if (is_numeric($value)) {
            $date = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value);
            return $date->format('Y-m-d');
        }

        // Tentar parsear como string
        try {
            return date('Y-m-d', strtotime($value));
        } catch (\Exception $e) {
            return date('Y-m-d');
        }
    }

    /**
     * Converter string de valor monetário para float
     */
    private function parseAmount($value): float
    {
        if (is_numeric($value)) {
            return (float) $value;
        }

        // Remover símbolos de moeda e espaços
        $value = preg_replace('/[€$R\s]/', '', $value);
        
        // Converter vírgula para ponto (formato europeu)
        $value = str_replace(',', '.', $value);
        
        // Remover pontos de milhar
        if (substr_count($value, '.') > 1) {
            $parts = explode('.', $value);
            $last = array_pop($parts);
            $value = implode('', $parts) . '.' . $last;
        }

        return (float) $value;
    }

    /**
     * Converter status do Excel para status do sistema
     */
    private function parseStatus($value): string
    {
        $value = strtolower(trim($value));
        
        $paidStatuses = ['abonado', 'paid', 'pago', 'pagado', 'liquidado'];
        $pendingStatuses = ['pendiente', 'pending', 'pendente', 'a pagar'];
        $overdueStatuses = ['atrasado', 'overdue', 'vencido', 'mora'];

        if (in_array($value, $paidStatuses)) {
            return LiabilityInstallment::STATUS_PAID;
        }
        if (in_array($value, $overdueStatuses)) {
            return LiabilityInstallment::STATUS_OVERDUE;
        }
        return LiabilityInstallment::STATUS_PENDING;
    }

    /**
     * Obter parcelas de uma conta passivo
     */
    public function installments(int $id): JsonResponse
    {
        $account = LiabilityAccount::where('user_id', Auth::id())->findOrFail($id);
        
        $installments = $account->installments()
            ->orderBy('installment_number')
            ->get();

        return response()->json([
            'success' => true,
            'data' => $installments,
            'statuses' => LiabilityInstallment::STATUSES,
        ]);
    }

    /**
     * Atualizar status de uma parcela
     */
    public function updateInstallment(Request $request, int $accountId, int $installmentId): JsonResponse
    {
        $account = LiabilityAccount::where('user_id', Auth::id())->findOrFail($accountId);
        $installment = LiabilityInstallment::where('liability_account_id', $account->id)
            ->findOrFail($installmentId);

        $validated = $request->validate([
            'status' => ['sometimes', Rule::in(array_keys(LiabilityInstallment::STATUSES))],
            'paid_amount' => 'nullable|numeric|min:0',
            'paid_date' => 'nullable|date',
            'payment_account_id' => 'nullable|exists:accounts,id',
            'notes' => 'nullable|string',
        ]);

        // Se marcar como pago
        if (isset($validated['status']) && $validated['status'] === 'paid') {
            $installment->markAsPaid(
                $validated['paid_amount'] ?? null,
                isset($validated['paid_date']) ? new \DateTime($validated['paid_date']) : null,
                $validated['payment_account_id'] ?? null
            );
        } else {
            $installment->update($validated);
            $account->recalculateTotals();
        }

        return response()->json([
            'success' => true,
            'message' => 'Parcela atualizada com sucesso',
            'data' => $installment->fresh(),
        ]);
    }

    /**
     * Obter resumo de todas as contas passivo
     */
    public function summary(): JsonResponse
    {
        $accounts = LiabilityAccount::where('user_id', Auth::id())
            ->where('is_active', true)
            ->get();

        // Agrupar por moeda
        $byCurrency = $accounts->groupBy('currency')->map(function ($group) {
            return [
                'total_principal' => $group->sum('principal_amount'),
                'total_paid' => $group->sum('total_paid'),
                'total_pending' => $group->sum('total_pending'),
                'total_interest' => $group->sum('total_interest'),
                'remaining_balance' => $group->sum('remaining_balance'),
                'contracts_count' => $group->count(),
            ];
        });

        // Próximas parcelas a vencer
        $upcomingInstallments = LiabilityInstallment::whereHas('liabilityAccount', function ($q) {
            $q->where('user_id', Auth::id())->where('is_active', true);
        })
            ->where('status', 'pending')
            ->where('due_date', '>=', now())
            ->where('due_date', '<=', now()->addDays(30))
            ->with('liabilityAccount:id,name,currency')
            ->orderBy('due_date')
            ->limit(10)
            ->get();

        // Parcelas atrasadas
        $overdueInstallments = LiabilityInstallment::whereHas('liabilityAccount', function ($q) {
            $q->where('user_id', Auth::id())->where('is_active', true);
        })
            ->where('status', '!=', 'paid')
            ->where('due_date', '<', now())
            ->with('liabilityAccount:id,name,currency')
            ->orderBy('due_date')
            ->get();

        return response()->json([
            'success' => true,
            'data' => [
                'by_currency' => $byCurrency,
                'upcoming_installments' => $upcomingInstallments,
                'overdue_installments' => $overdueInstallments,
                'overdue_count' => $overdueInstallments->count(),
            ],
        ]);
    }

    /**
     * Conciliar uma parcela com uma transação existente
     * 
     * Vincula uma parcela de conta passivo a uma transação já registrada
     */
    public function reconcile(Request $request, int $accountId, int $installmentId): JsonResponse
    {
        $account = LiabilityAccount::where('user_id', Auth::id())->findOrFail($accountId);
        $installment = LiabilityInstallment::where('liability_account_id', $account->id)
            ->findOrFail($installmentId);

        $validated = $request->validate([
            'transaction_id' => 'required|exists:transactions,id',
            'mark_as_paid' => 'nullable|boolean',
        ]);

        // Verificar se a transação pertence ao usuário
        $transaction = \App\Models\Transaction::where('user_id', Auth::id())
            ->findOrFail($validated['transaction_id']);

        try {
            DB::beginTransaction();

            // Atualizar parcela com referência à transação
            $installment->reconciled_transaction_id = $transaction->id;
            $installment->payment_account_id = $transaction->account_id;
            
            // Opcionalmente marcar como paga
            if ($request->boolean('mark_as_paid', true)) {
                $installment->status = LiabilityInstallment::STATUS_PAID;
                $installment->paid_amount = abs($transaction->amount);
                $installment->paid_date = $transaction->date;
            }
            
            $installment->save();

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

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Parcela conciliada com sucesso',
                'data' => $installment->fresh()->load('liabilityAccount'),
            ]);

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

    /**
     * Remover conciliação de uma parcela
     */
    public function unreconcile(int $accountId, int $installmentId): JsonResponse
    {
        $account = LiabilityAccount::where('user_id', Auth::id())->findOrFail($accountId);
        $installment = LiabilityInstallment::where('liability_account_id', $account->id)
            ->findOrFail($installmentId);

        if (!$installment->reconciled_transaction_id) {
            return response()->json([
                'success' => false,
                'message' => 'Parcela não está conciliada',
            ], 422);
        }

        try {
            DB::beginTransaction();

            // Calcular o sobrepagamento que foi registrado (paid_amount - installment_amount)
            $paidAmount = (float) $installment->paid_amount;
            $plannedAmount = (float) $installment->installment_amount;
            $overpaymentToRemove = max(0, $paidAmount - $plannedAmount);

            // Remover referência à transação
            $installment->reconciled_transaction_id = null;
            $installment->status = LiabilityInstallment::STATUS_PENDING;
            $installment->paid_amount = 0;
            $installment->paid_date = null;
            
            // Remover o cargo extra (sobrepagamento) que foi adicionado na conciliação
            if ($overpaymentToRemove > 0 && $installment->fee_amount >= $overpaymentToRemove) {
                $installment->fee_amount = $installment->fee_amount - $overpaymentToRemove;
            }
            
            $installment->save();

            // Recalcular totais
            $account->recalculateTotals();

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Conciliação removida com sucesso',
                'data' => $installment->fresh(),
                'fee_removed' => $overpaymentToRemove > 0 ? $overpaymentToRemove : null,
            ]);

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

    /**
     * Buscar transações elegíveis para conciliação
     * 
     * Retorna transações que podem ser vinculadas a uma parcela
     * Ordenadas por similaridade de valor com a parcela
     */
    public function eligibleTransactions(Request $request, int $accountId, int $installmentId): JsonResponse
    {
        $account = LiabilityAccount::where('user_id', Auth::id())->findOrFail($accountId);
        $installment = LiabilityInstallment::where('liability_account_id', $account->id)
            ->findOrFail($installmentId);

        // Buscar transações dentro de uma janela de tempo (+/- 45 dias da data de vencimento)
        // Janela ampla para capturar pagamentos atrasados ou antecipados
        $startDate = (clone $installment->due_date)->subDays(45);
        $endDate = (clone $installment->due_date)->addDays(45);
        
        $installmentAmount = (float) $installment->installment_amount;

        // Usar effective_date se existir, senão planned_date
        $query = \App\Models\Transaction::where('user_id', Auth::id())
            ->where(function ($q) use ($startDate, $endDate) {
                $q->whereBetween('effective_date', [$startDate, $endDate])
                  ->orWhere(function ($q2) use ($startDate, $endDate) {
                      $q2->whereNull('effective_date')
                         ->whereBetween('planned_date', [$startDate, $endDate]);
                  });
            })
            ->where('type', 'debit') // Pagamentos são débitos (saídas)
            ->with('account:id,name,currency');

        // Por padrão, filtrar por valores próximos (±20% do valor da parcela)
        // Permite encontrar transações mesmo com pequenas diferenças
        $minAmount = $installmentAmount * 0.8;
        $maxAmount = $installmentAmount * 1.2;
        
        // Se strict_amount = false ou não informado, ainda assim filtrar por faixa
        // Usa COALESCE para considerar amount ou planned_amount
        if (!$request->has('no_amount_filter')) {
            $query->whereRaw('COALESCE(amount, planned_amount) BETWEEN ? AND ?', [$minAmount, $maxAmount]);
        }

        // Se tiver filtro por conta específica
        if ($request->has('account_id')) {
            $query->where('account_id', $request->account_id);
        }

        // Busca por descrição
        if ($request->has('search')) {
            $query->where(function ($q) use ($request) {
                $q->where('description', 'like', '%' . $request->search . '%')
                  ->orWhere('original_description', 'like', '%' . $request->search . '%');
            });
        }

        // Ordenar por similaridade de valor (mais próximo primeiro) e depois por data
        // ABS(COALESCE(amount, planned_amount) - valor_parcela) = diferença absoluta
        $query->orderByRaw("ABS(COALESCE(amount, planned_amount) - ?) ASC", [$installmentAmount])
              ->orderByRaw('COALESCE(effective_date, planned_date) DESC');

        $transactions = $query->limit(30)->get();

        // Adicionar campo de diferença percentual para cada transação
        $transactions->transform(function ($transaction) use ($installmentAmount) {
            $transactionAmount = (float) ($transaction->amount ?? $transaction->planned_amount);
            $diff = abs($transactionAmount - $installmentAmount);
            $diffPercent = $installmentAmount > 0 ? ($diff / $installmentAmount) * 100 : 0;
            $transaction->amount_difference = round($diff, 2);
            $transaction->amount_difference_percent = round($diffPercent, 1);
            return $transaction;
        });

        return response()->json([
            'success' => true,
            'data' => $transactions,
            'installment' => [
                'id' => $installment->id,
                'installment_number' => $installment->installment_number,
                'due_date' => $installment->due_date->format('Y-m-d'),
                'installment_amount' => $installmentAmount,
            ],
            'search_period' => [
                'start' => $startDate->format('Y-m-d'),
                'end' => $endDate->format('Y-m-d'),
            ],
            'amount_range' => [
                'min' => round($minAmount, 2),
                'max' => round($maxAmount, 2),
            ],
        ]);
    }

    /**
     * Listar parcelas pendentes de conciliação
     */
    public function pendingReconciliation(): JsonResponse
    {
        $installments = LiabilityInstallment::whereHas('liabilityAccount', function ($q) {
            $q->where('user_id', Auth::id())->where('is_active', true);
        })
            ->whereNull('reconciled_transaction_id')
            ->where('status', '!=', 'cancelled')
            ->with('liabilityAccount:id,name,currency,creditor')
            ->orderBy('due_date')
            ->get();

        return response()->json([
            'success' => true,
            'data' => $installments,
            'count' => $installments->count(),
        ]);
    }
}
