<?php

namespace App\Settlement\Service;

use App\Auth\Entity\User;
use App\Settlement\Entity\WalletTransaction;
use App\Settlement\Repository\SettlementRepository;
use App\Settlement\Repository\WalletTransactionRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use App\UserProfile\Repository\UserProfileRepository;

/**
 * منطق کیف پولِ بیمار: موجودی، شارژ (credit)، برداشت (debit) و تسویهٔ سرویس از
 * کیف پول — همگی با ثبتِ کاربرِ عامل، روش پرداخت و دلیل برای شفافیت کامل.
 * موجودی همیشه از مجموع credit − debit مشتق می‌شود (SettlementRepository).
 *
 * موجودی **سراسری** است چون کیف پول مالِ شخص است، ولی هر تراکنش محیطِ ثبتش را
 * هم نگه می‌دارد تا دفترِ تراکنشی که کلینیک می‌بیند به همان محیط محدود شود.
 * فراخوانی بدون محیط (سهم نماینده، برداشت صاحب حساب) عمداً تهی می‌ماند.
 */
class WalletService
{
    public function __construct(
        private readonly WalletTransactionRepository $walletRepo,
        private readonly SettlementRepository        $settlementRepo,
        private readonly UserProfileRepository       $profileRepo,
    ) {}

    public function balance(User $patient): int
    {
        return $this->settlementRepo->getWalletBalance($patient);
    }

    /** نامِ نمایشیِ کاربرِ عامل (برای ستون «ثبت‌کننده»)؛ در نبودِ پروفایل، شماره موبایل. */
    public function resolveActorName(?User $actor): ?string
    {
        if ($actor === null) {
            return null;
        }
        $profile = $this->profileRepo->findByUser($actor);
        $name = trim(($profile?->getLabel() ?? '') . ' ' . ($profile?->getFamily() ?? ''));

        return $name !== '' ? $name : $actor->getMobileNumber();
    }

    /** شارژِ کیف پول (credit). فرض بر مثبت بودنِ مبلغ است (اعتبارسنجی در Controller). */
    public function charge(
        User $patient,
        int $amountRials,
        ?User $actor = null,
        ?string $description = null,
        ?string $paymentMethod = null,
        ?string $reference = null,
        ?string $entityType = null,
        ?int $entityId = null,
    ): WalletTransaction {
        $balanceAfter = $this->balance($patient) + $amountRials;

        return $this->record(
            $patient, $amountRials, WalletTransaction::TYPE_CREDIT, $balanceAfter, $actor,
            $description !== null && $description !== '' ? $description : 'شارژ کیف پول',
            $paymentMethod, $reference, $entityType, $entityId,
        );
    }

    /**
     * برداشت از کیف پول (debit). اگر مبلغ از موجودی بیشتر باشد
     * AppException با کد ERR_WALLET_INSUFFICIENT (۴۲۲) پرتاب می‌شود.
     */
    public function withdraw(
        User $patient,
        int $amountRials,
        ?User $actor = null,
        ?string $description = null,
        ?string $paymentMethod = null,
        ?string $reference = null,
        ?string $entityType = null,
        ?int $entityId = null,
    ): WalletTransaction {
        $current = $this->balance($patient);
        if ($amountRials > $current) {
            throw new AppException(ErrorCodes::ERR_WALLET_INSUFFICIENT, null, 422, 'amount_rials');
        }

        return $this->record(
            $patient, $amountRials, WalletTransaction::TYPE_DEBIT, $current - $amountRials, $actor,
            $description !== null && $description !== '' ? $description : 'برداشت از کیف پول',
            $paymentMethod, $reference, $entityType, $entityId,
        );
    }

    private function record(
        User $patient,
        int $amountRials,
        string $type,
        int $balanceAfter,
        ?User $actor,
        string $description,
        ?string $paymentMethod,
        ?string $reference,
        ?string $entityType,
        ?int $entityId,
    ): WalletTransaction {
        $txn = new WalletTransaction($patient, $amountRials, $type, $balanceAfter);
        $txn->setRecordedEnvironment($entityType, $entityId)
            ->setDescription($description)
            ->setCreatedBy($actor)
            ->setCreatedByName($this->resolveActorName($actor))
            ->setPaymentMethod($paymentMethod)
            ->setReference($reference)
            ->setStatus(WalletTransaction::STATUS_CONFIRMED);
        $this->walletRepo->save($txn);

        return $txn;
    }
}
