<?php

namespace App\Services;

use App\Models\Invoice;
use App\Models\Payment;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class FinanceService
{
    /**
     * Generate a unique, human-readable invoice number.
     */
    public function generateInvoiceNumber(): string
    {
        return 'INV-'.now()->format('Ymd').'-'.strtoupper(Str::random(6));
    }

    /**
     * Generate a unique transaction ID.
     */
    public function generateTransactionId(string $prefix = 'TXN'): string
    {
        return $prefix.'-'.now()->format('Ymd').'-'.strtoupper(Str::random(6));
    }

    /**
     * Calculate invoice totals from line items.
     *
     * @param  array  $items  Array of items with quantity, unit_price, tax_rate
     * @return array{subtotal: float, tax: float, total: float, items: array}
     */
    public function calculateInvoiceTotals(array $items): array
    {
        $subtotal = 0;
        $totalTax = 0;
        $itemsData = [];

        foreach ($items as $item) {
            $quantity = $item['quantity'];
            $unitPrice = $item['unit_price'];
            $taxRate = $item['tax_rate'] ?? 0;

            $lineTotal = $quantity * $unitPrice;
            $taxAmount = round($lineTotal * ($taxRate / 100), 2);

            $subtotal += $lineTotal;
            $totalTax += $taxAmount;

            $itemsData[] = [
                'description' => $item['description'],
                'quantity' => $quantity,
                'unit_price' => $unitPrice,
                'total_price' => $lineTotal,
                'tax_rate' => $taxRate,
                'tax_amount' => $taxAmount,
                'vehicle_id' => $item['vehicle_id'] ?? null,
            ];
        }

        return [
            'subtotal' => round($subtotal, 2),
            'tax' => round($totalTax, 2),
            'total' => round($subtotal + $totalTax, 2),
            'items' => $itemsData,
        ];
    }

    /**
     * Create an invoice with calculated totals and line items.
     */
    public function createInvoice(array $data, array $items): Invoice
    {
        $totals = $this->calculateInvoiceTotals($items);

        $invoice = Invoice::create([
            'invoice_number' => $this->generateInvoiceNumber(),
            'customer_id' => $data['customer_id'],
            'branch_id' => $data['branch_id'] ?? null,
            'vehicle_id' => $data['vehicle_id'] ?? null,
            'type' => $data['type'],
            'status' => 'DRAFT',
            'payment_status' => 'PENDING',
            'issue_date' => $data['issue_date'],
            'due_date' => $data['due_date'],
            'subtotal' => $totals['subtotal'],
            'tax_amount' => $totals['tax'],
            'total_amount' => $totals['total'],
            'paid_amount' => 0,
            'notes' => $data['notes'] ?? null,
            'terms' => $data['terms'] ?? null,
            'created_by' => auth()->id(),
        ]);

        $invoice->items()->createMany($totals['items']);

        return $invoice;
    }

    /**
     * Record a payment and update the linked invoice (if any) atomically.
     */
    public function recordPayment(array $data): Payment
    {
        return DB::transaction(function () use ($data) {
            $invoice = null;

            if (! empty($data['invoice_id'])) {
                $invoice = Invoice::query()->lockForUpdate()->findOrFail($data['invoice_id']);

                if ((string) $invoice->customer_id !== (string) $data['customer_id']) {
                    throw ValidationException::withMessages([
                        'invoice_id' => 'The selected invoice does not belong to the provided customer.',
                    ]);
                }

                $remaining = max((float) $invoice->total_amount - (float) $invoice->paid_amount, 0);
                if ((float) $data['amount'] > $remaining) {
                    throw ValidationException::withMessages([
                        'amount' => 'Payment amount exceeds invoice remaining balance.',
                    ]);
                }
            }

            $payment = Payment::create([
                'customer_id' => $data['customer_id'],
                'amount' => $data['amount'],
                'payment_method' => $data['payment_method'],
                'transaction_id' => $data['transaction_id'] ?? $this->generateTransactionId(),
                'invoice_id' => $data['invoice_id'] ?? null,
                'status' => 'COMPLETED',
                'currency' => $data['currency'] ?? 'EGP',
                'notes' => $data['notes'] ?? null,
                'branch_id' => $data['branch_id'] ?? null,
            ]);

            if ($invoice !== null) {
                $this->updateInvoiceAfterPayment($invoice, $payment);
            }

            return $payment;
        });
    }

    /**
     * Update invoice paid_amount and status after a payment, with pessimistic locking.
     */
    private function updateInvoiceAfterPayment(Invoice $invoice, Payment $payment): void
    {
        $invoice->paid_amount = round((float) $invoice->paid_amount + (float) $payment->amount, 2);
        $newStatus = $invoice->paid_amount >= (float) $invoice->total_amount ? 'PAID' : 'PARTIALLY_PAID';
        $invoice->update([
            'paid_amount' => $invoice->paid_amount,
            'status' => $newStatus,
            'payment_status' => $newStatus,
        ]);
    }
}
