<?php

namespace App\Package\Entity;

use App\Appointment\Entity\Appointment;
use App\Auth\Entity\User;
use App\ClinicService\Entity\ServiceItem;
use App\Package\Repository\SessionCreditLedgerRepository;
use App\Shared\Tenant\TenantOwnedTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

/**
 * دفتر اعتبار جلسات — **append-only**.
 *
 * ردیف‌ها هرگز حذف یا ویرایش نمی‌شوند؛ تصحیح یعنی ردیف تازه. مانده جمع `delta` هاست،
 * پس هر عددی که کاربر می‌بیند یک تاریخچهٔ کامل پشتش دارد و «۳ جلسه‌ام چه شد؟» همیشه
 * جواب دارد.
 *
 * `uniq_ledger_appointment_kind` مصرف دوباره را می‌بندد: `confirm` idempotent است و
 * اجرای دومش نباید جلسهٔ دوم را بخورد.
 */
#[ORM\Entity(repositoryClass: SessionCreditLedgerRepository::class)]
#[ORM\Table(name: 'session_credit_ledger')]
#[ORM\Index(columns: ['patient_package_id', 'created_at'], name: 'idx_scl_package')]
#[ORM\Index(columns: ['entity_type', 'entity_id', 'created_at'], name: 'idx_scl_tenant')]
#[ORM\Index(columns: ['appointment_id'], name: 'idx_scl_appt')]
#[ORM\UniqueConstraint(name: 'uniq_scl_consume', columns: ['appointment_id', 'kind'])]
class SessionCreditLedger
{
    use TenantOwnedTrait;

    public const KIND_PURCHASE   = 'purchase';
    public const KIND_CONSUME    = 'consume';
    public const KIND_REFUND     = 'refund';
    public const KIND_ADJUSTMENT = 'adjustment';
    public const KIND_EXPIRY     = 'expiry';

    public const KINDS = [
        self::KIND_PURCHASE,
        self::KIND_CONSUME,
        self::KIND_REFUND,
        self::KIND_ADJUSTMENT,
        self::KIND_EXPIRY,
    ];

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

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

    #[ORM\ManyToOne(targetEntity: PatientPackage::class)]
    #[ORM\JoinColumn(name: 'patient_package_id', nullable: false, onDelete: 'RESTRICT')]
    private PatientPackage $patientPackage;

    #[ORM\Column(type: 'string', length: 15)]
    private string $kind;

    /** مثبت یا منفی — هرگز صفر: ردیفی که چیزی را عوض نمی‌کند فقط نویز است. */
    #[ORM\Column(type: 'smallint')]
    private int $delta;

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

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

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $reason = null;

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

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

    public function __construct(
        PatientPackage $patientPackage,
        string $kind,
        int $delta,
        ?Appointment $appointment = null,
        ?ServiceItem $serviceItem = null,
        ?string $reason = null,
        ?User $createdBy = null,
    ) {
        if (!in_array($kind, self::KINDS, true)) {
            throw new \InvalidArgumentException(sprintf('Unknown ledger kind "%s".', $kind));
        }

        if ($delta === 0) {
            throw new \InvalidArgumentException('A ledger row with a zero delta changes nothing.');
        }

        $this->uuid           = Uuid::v4()->toRfc4122();
        $this->patientPackage = $patientPackage;
        $this->kind           = $kind;
        $this->delta          = $delta;
        $this->appointment    = $appointment;
        $this->serviceItem    = $serviceItem;
        $this->reason         = $reason;
        $this->createdBy      = $createdBy;
        $this->createdAt      = time();

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

    public function getId(): ?string                 { return $this->id; }
    public function getUuid(): string                { return $this->uuid; }
    public function getPatientPackage(): PatientPackage { return $this->patientPackage; }
    public function getKind(): string                { return $this->kind; }
    public function getDelta(): int                  { return $this->delta; }
    public function getAppointment(): ?Appointment   { return $this->appointment; }
    public function getServiceItem(): ?ServiceItem   { return $this->serviceItem; }
    public function getReason(): ?string             { return $this->reason; }
    public function getCreatedBy(): ?User            { return $this->createdBy; }
    public function getCreatedAt(): int              { return $this->createdAt; }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'uuid'             => $this->uuid,
            'kind'             => $this->kind,
            'delta'            => $this->delta,
            'appointment_uuid' => $this->appointment?->getUuid(),
            'service_uuid'     => $this->serviceItem?->getUuid(),
            'service_name'     => $this->serviceItem?->getName(),
            'reason'           => $this->reason,
            'created_by'       => $this->createdBy?->getUuid(),
            'created_at'       => $this->createdAt,
        ];
    }
}
