<?php

namespace App\Core\Services\Payment;

use App\Core\Repositories\InvoiceRepository;
use App\Core\Services\Receipt\ReceiptService;
use App\Models\Invoice;
use App\Models\Payment;
use App\Services\ReminderService;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * Payment Service
 * Handles payment processing and invoice balance updates
 */
class PaymentService
{
    public function __construct(
        private InvoiceRepository $invoiceRepository,
        private ReceiptService $receiptService,
        private ReminderService $reminderService
    ) {}

    /**
     * Record a payment
     */
    public function recordPayment(Invoice $invoice, array $data): Payment
    {
        DB::beginTransaction();

        try {
            // Generate payment number
            $paymentNumber = $this->generatePaymentNumber();

            // Create payment
            $payment = Payment::create([
                'payment_number' => $paymentNumber,
                'invoice_id' => $invoice->id,
                'customer_id' => $invoice->customer_id,
                'amount' => $data['amount'],
                'currency' => $data['currency'] ?? $invoice->currency,
                'payment_method' => $data['payment_method'] ?? 'bank_transfer',
                'bank_account_id' => $data['bank_account_id'] ?? null,
                'status' => $data['status'] ?? 'completed',
                'payment_date' => $data['payment_date'] ?? now(),
                'notes' => $data['notes'] ?? null,
                'transaction_id' => $data['transaction_id'] ?? null,
                'reference' => $data['reference'] ?? null,
                'created_by' => auth()->id(),
            ]);

            // Update invoice balance
            // Note: For pending payments from public, balance is updated but status remains pending for admin review
            $this->updateInvoiceBalance($invoice, $payment->amount);

            DB::commit();

            $payment = $payment->load(['invoice', 'customer']);
            $invoiceFresh = $invoice->fresh();
            if ($invoiceFresh) {
                try {
                    if ($invoiceFresh->isPaid()) {
                        $this->reminderService->sendInvoicePaid($invoiceFresh);
                        $this->reminderService->sendPaymentReceipt($payment);
                    } else {
                        $this->reminderService->sendPartialPayment($payment);
                    }
                } catch (\Throwable $e) {
                    Log::warning('Billing reminder after payment: '.$e->getMessage());
                }
            }

            return $payment;
        } catch (Exception $e) {
            DB::rollBack();
            Log::error('Payment recording failed: '.$e->getMessage());
            throw $e;
        }
    }

    /**
     * Update invoice balance after payment
     */
    private function updateInvoiceBalance(Invoice $invoice, float $amount): void
    {
        $newPaidAmount = $invoice->paid_amount + $amount;
        $newBalance = $invoice->total - $newPaidAmount;

        $status = 'partial';
        if ($newBalance <= 0) {
            $status = 'paid';
            $newBalance = 0;
            $newPaidAmount = $invoice->total;
        }

        $this->invoiceRepository->update($invoice, [
            'paid_amount' => $newPaidAmount,
            'balance' => $newBalance,
            'status' => $status,
        ]);

        // Auto-generate receipt when invoice is fully paid
        if ($status === 'paid') {
            // Refresh invoice to get latest data
            $invoice->refresh();

            // Check if receipt doesn't exist
            if (! $invoice->receipt) {
                try {
                    $this->receiptService->generateFromInvoice($invoice);
                } catch (Exception $e) {
                    // Log error but don't fail the payment
                    Log::warning('Auto-receipt generation failed: '.$e->getMessage());
                }
            }
        }
    }

    /**
     * Generate payment number
     */
    private function generatePaymentNumber(): string
    {
        $prefix = config('invoice.payment_prefix', 'PAY');
        $year = now()->year;
        $lastPayment = Payment::whereYear('created_at', $year)
            ->orderBy('id', 'desc')
            ->first();

        if ($lastPayment) {
            $lastNumber = (int) substr($lastPayment->payment_number, -6);
            $nextNumber = $lastNumber + 1;
        } else {
            $nextNumber = 1;
        }

        return $prefix.'-'.$year.'-'.str_pad($nextNumber, 6, '0', STR_PAD_LEFT);
    }

    /**
     * Refund a payment
     */
    public function refundPayment(Payment $payment): bool
    {
        DB::beginTransaction();

        try {
            $payment->update(['status' => 'refunded']);

            $invoice = $payment->invoice;
            $newPaidAmount = max(0, $invoice->paid_amount - $payment->amount);
            $newBalance = $invoice->total - $newPaidAmount;

            $status = $newBalance > 0 ? 'partial' : 'paid';

            $this->invoiceRepository->update($invoice, [
                'paid_amount' => $newPaidAmount,
                'balance' => $newBalance,
                'status' => $status,
            ]);

            DB::commit();

            return true;
        } catch (Exception $e) {
            DB::rollBack();
            Log::error('Payment refund failed: '.$e->getMessage());
            throw $e;
        }
    }
}
