<?php

namespace App\Services;

use App\Models\DeliveryPartner;
use App\Models\WalletTransaction;
use Illuminate\Support\Facades\DB;

/**
 * Tracks a courier's two money buckets with a full ledger (WalletTransaction):
 *  - wallet_balance : delivery-fee earnings the platform OWES the courier
 *  - pending_cod    : cash-on-delivery the courier collected and HOLDS (owes the platform)
 *
 * Earnings + COD are recorded automatically on delivery; payouts (platform → courier)
 * and COD remittances (courier → platform) are recorded by the admin.
 */
class CourierWalletService
{
    /** Credit a delivery-fee earning to the courier's payable wallet (on delivery). */
    public function creditEarning(DeliveryPartner $partner, float $amount, ?string $reference = null, ?string $description = null): void
    {
        if ($amount <= 0) {
            return;
        }
        DB::transaction(function () use ($partner, $amount, $reference, $description) {
            $before = (float) $partner->wallet_balance;
            $partner->increment('wallet_balance', $amount);
            $this->log($partner, 'credit', 'delivery_earning', $amount, $before, $before + $amount, $reference, $description ?? 'Delivery earning');
        });
    }

    /** Record COD cash the courier collected from the customer (a liability they hold). */
    public function addCod(DeliveryPartner $partner, float $amount, ?string $reference = null, ?string $description = null): void
    {
        if ($amount <= 0) {
            return;
        }
        DB::transaction(function () use ($partner, $amount, $reference, $description) {
            $before = (float) $partner->pending_cod;
            $partner->increment('pending_cod', $amount);
            $this->log($partner, 'credit', 'cod_collected', $amount, $before, $before + $amount, $reference, $description ?? 'COD collected');
        });
    }

    /** Admin pays out the courier's earned wallet balance. */
    public function recordPayout(DeliveryPartner $partner, float $amount, ?string $reference = null, ?string $description = null): WalletTransaction
    {
        return DB::transaction(function () use ($partner, $amount, $reference, $description) {
            $before = (float) $partner->wallet_balance;
            abort_if($amount <= 0, 422, 'Payout amount must be positive.');
            abort_if($amount > $before + 0.001, 422, "Payout exceeds the courier's wallet balance.");
            $partner->decrement('wallet_balance', $amount);
            return $this->log($partner, 'debit', 'payout', $amount, $before, $before - $amount, $reference, $description ?? 'Earnings payout');
        });
    }

    /** Admin records cash the courier handed back — clears their outstanding COD. */
    public function recordCodRemittance(DeliveryPartner $partner, float $amount, ?string $reference = null, ?string $description = null): WalletTransaction
    {
        return DB::transaction(function () use ($partner, $amount, $reference, $description) {
            $before = (float) $partner->pending_cod;
            abort_if($amount <= 0, 422, 'Remittance amount must be positive.');
            abort_if($amount > $before + 0.001, 422, "Remittance exceeds the courier's outstanding COD.");
            $partner->decrement('pending_cod', $amount);
            return $this->log($partner, 'debit', 'cod_remittance', $amount, $before, $before - $amount, $reference, $description ?? 'COD cash remitted');
        });
    }

    private function log(DeliveryPartner $partner, string $type, string $purpose, float $amount, float $before, float $after, ?string $reference, ?string $description): WalletTransaction
    {
        return WalletTransaction::create([
            'owner_type'     => DeliveryPartner::class,
            'owner_id'       => $partner->id,
            'type'           => $type,
            'purpose'        => $purpose,
            'amount'         => $amount,
            'balance_before' => round($before, 2),
            'balance_after'  => round($after, 2),
            'reference'      => $reference,
            'description'    => $description,
        ]);
    }
}
