<?php

namespace App\Package\Entity;

use App\Package\Repository\PatientPackageRepository;
use App\Patient\Entity\PatientRecord;
use App\Payment\Entity\Payment;
use App\Shared\Tenant\TenantOwnedTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

/**
 * پکیجی که یک بیمار خریده.
 *
 * ⛔ **هیچ ستون مانده‌ای اینجا نیست و نباید باشد.** `sessionCount` فقط snapshotِ
 * تعریف لحظهٔ خرید است؛ مانده همیشه از جمع ردیف‌های {@see SessionCreditLedger}
 * می‌آید. مستند صریح است: «اگر فقط یک عدد نگه داریم، اولین اشتباه هرگز قابل
 * ردیابی نیست.»
 */
#[ORM\Entity(repositoryClass: PatientPackageRepository::class)]
#[ORM\Table(name: 'patient_packages')]
#[ORM\Index(columns: ['entity_type', 'entity_id', 'purchased_at'], name: 'idx_pp_tenant')]
#[ORM\Index(columns: ['patient_record_id', 'valid_to'], name: 'idx_pp_patient')]
class PatientPackage
{
    use TenantOwnedTrait;

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\Column(type: 'string', length: 36, unique: true)]
    private string $uuid;

    #[ORM\ManyToOne(targetEntity: Package::class)]
    #[ORM\JoinColumn(name: 'package_id', nullable: false, onDelete: 'RESTRICT')]
    private Package $package;

    #[ORM\ManyToOne(targetEntity: PatientRecord::class)]
    #[ORM\JoinColumn(name: 'patient_record_id', nullable: false, onDelete: 'RESTRICT')]
    private PatientRecord $patientRecord;

    /** snapshot تعریف — نه مانده. */
    #[ORM\Column(name: 'session_count', type: 'smallint')]
    private int $sessionCount;

    #[ORM\Column(name: 'price_paid_rials', type: 'bigint')]
    private string|int $pricePaidRials;

    #[ORM\ManyToOne(targetEntity: Payment::class)]
    #[ORM\JoinColumn(name: 'payment_id', nullable: true, onDelete: 'SET NULL')]
    private ?Payment $payment = null;

    /** مبنای FIFO. */
    #[ORM\Column(name: 'purchased_at', type: 'integer')]
    private int $purchasedAt;

    #[ORM\Column(name: 'valid_to', type: 'integer', nullable: true)]
    private ?int $validTo = null;

    #[ORM\Column(name: 'created_at', type: 'integer')]
    private int $createdAt;

    #[ORM\Column(name: 'updated_at', type: 'integer')]
    private int $updatedAt;

    public function __construct(Package $package, PatientRecord $patientRecord, ?int $purchasedAt = null)
    {
        $this->uuid            = Uuid::v4()->toRfc4122();
        $this->package         = $package;
        $this->patientRecord   = $patientRecord;
        $this->purchasedAt     = $purchasedAt ?? time();
        $this->sessionCount    = $package->getSessionCount();
        $this->pricePaidRials  = $package->getPriceRials();
        $this->validTo         = $package->expiryFor($this->purchasedAt);
        $this->createdAt       = time();
        $this->updatedAt       = time();

        $this->assignTenantPair($package->getEntityType(), $package->getEntityId());
    }

    public function getId(): ?int                    { return $this->id; }
    public function getUuid(): string                { return $this->uuid; }
    public function getPackage(): Package            { return $this->package; }
    public function getPatientRecord(): PatientRecord { return $this->patientRecord; }
    public function getSessionCount(): int           { return $this->sessionCount; }
    public function getPricePaidRials(): int         { return (int) $this->pricePaidRials; }
    public function getPayment(): ?Payment           { return $this->payment; }
    public function getPurchasedAt(): int            { return $this->purchasedAt; }
    public function getValidTo(): ?int               { return $this->validTo; }

    public function setPricePaidRials(int $v): self { $this->pricePaidRials = max(0, $v); $this->updatedAt = time(); return $this; }
    public function setPayment(?Payment $v): self   { $this->payment = $v; $this->updatedAt = time(); return $this; }

    public function isExpired(?int $at = null): bool
    {
        return $this->validTo !== null && $this->validTo < ($at ?? time());
    }

    /**
     * @param int $balance مانده‌ای که فراخوان از دفتر گرفته — عمداً پارامتر است، نه
     *                     چیزی که این کلاس خودش بداند
     * @return array<string, mixed>
     */
    public function toArray(int $balance): array
    {
        return [
            'uuid'             => $this->uuid,
            'package_uuid'     => $this->package->getUuid(),
            'package_name'     => $this->package->getName(),
            'patient_uuid'     => $this->patientRecord->getUuid(),
            'session_count'    => $this->sessionCount,
            'price_paid_rials' => (int) $this->pricePaidRials,
            'purchased_at'     => $this->purchasedAt,
            'valid_to'         => $this->validTo,
            'expired'          => $this->isExpired(),
            // مانده در نمایشِ پکیج منقضی صفر است، حتی اگر ردیف `expiry` هنوز ثبت
            // نشده باشد؛ دفتر خودش دست‌نخورده می‌ماند.
            'balance'          => $this->isExpired() ? 0 : $balance,
        ];
    }
}
