<?php

namespace App\Services;

use App\Enums\BillingStatus;
use App\Models\Billing;
use Illuminate\Support\Facades\DB;

/**
 * Transisi pembayaran billing ke Lunas secara atomik & idempoten (NFR-04).
 *
 * Satu-satunya jalur yang menandai billing Lunas + menambah credit.
 * Dipakai oleh konfirmasi manual admin DAN callback Duitku, sehingga
 * pembayaran ganda (manual + callback, atau callback berulang) aman.
 */
class BillingPaymentService
{
    public function __construct(
        private readonly CreditService $creditService,
    ) {}

    /**
     * Tandai billing Lunas + tambah credit. Idempoten.
     *
     * @param  array{source?: string, reference?: ?string, payload?: ?array}  $meta
     * @return bool true bila baru saja dibayar, false bila sudah Lunas sebelumnya.
     */
    public function markPaid(Billing $billing, array $meta = []): bool
    {
        return DB::transaction(function () use ($billing, $meta): bool {
            // Kunci baris billing agar transisi tidak race.
            $locked = Billing::whereKey($billing->id)->lockForUpdate()->firstOrFail();

            if ($locked->status === BillingStatus::Lunas) {
                return false; // sudah dibayar — no-op idempoten
            }

            if (! $locked->status->isPending()) {
                throw new \DomainException('Hanya invoice Pending yang dapat ditandai Lunas.');
            }

            $locked->update([
                'status' => BillingStatus::Lunas,
                'paid_at' => now(),
                'duitku_reference' => $meta['reference'] ?? $locked->duitku_reference,
                'duitku_callback_payload' => $meta['payload'] ?? $locked->duitku_callback_payload,
                'duitku_callback_received_at' => isset($meta['payload']) ? now() : $locked->duitku_callback_received_at,
            ]);

            $this->creditService->add($locked->user, $locked->jumlah_credit, [
                'invoice' => $locked->no_invoice,
                'keterangan' => "Deposit credit invoice {$locked->no_invoice} ({$locked->jumlah_credit} credit)",
            ]);

            // Sinkronkan instance pemanggil.
            $billing->status = BillingStatus::Lunas;
            $billing->paid_at = $locked->paid_at;

            return true;
        });
    }
}
