<?php

namespace App\Cancellation\Entity;

use App\Cancellation\Repository\CancellationPolicyRepository;
use App\ClinicService\Entity\ServiceItem;
use App\Shared\Tenant\TenantOwnedTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

/**
 * سیاست لغو یک محیط، یا override یک سرویس.
 *
 * `serviceItem === null` یعنی پیش‌فرض محیط. سرویسی که سیاست خودش را دارد، همان برنده
 * است — بدون ترکیب و بدون وراثت جزئی، چون «۲۴ ساعت از محیط ولی ۵۰٪ از سرویس» چیزی
 * است که هیچ اپراتوری نمی‌تواند در ذهنش شبیه‌سازی کند.
 *
 * پیش‌فرض عمداً **بدون جریمه** است: اگر پیش‌فرض جریمه‌دار بود، لحظهٔ deploy همهٔ
 * بیمارانِ با نوبت نزدیک مشمول جریمه می‌شدند و کلینیک خبر نداشت.
 */
#[ORM\Entity(repositoryClass: CancellationPolicyRepository::class)]
#[ORM\Table(name: 'cancellation_policies')]
#[ORM\UniqueConstraint(name: 'uniq_cancel_policy_scope', columns: ['entity_type', 'entity_id', 'service_item_id'])]
#[ORM\Index(columns: ['entity_type', 'entity_id', 'active'], name: 'idx_cancel_policies_tenant')]
class CancellationPolicy
{
    use TenantOwnedTrait;

    public const MODE_NONE    = 'none';
    public const MODE_PERCENT = 'percent';
    public const MODE_FIXED   = 'fixed';

    public const MODES = [self::MODE_NONE, self::MODE_PERCENT, self::MODE_FIXED];

    #[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: ServiceItem::class)]
    #[ORM\JoinColumn(name: 'service_item_id', nullable: true, onDelete: 'CASCADE')]
    private ?ServiceItem $serviceItem = null;

    #[ORM\Column(name: 'free_window_hours', type: 'smallint', options: ['default' => 24])]
    private int $freeWindowHours = 24;

    #[ORM\Column(name: 'penalty_mode', type: 'string', length: 10, options: ['default' => self::MODE_NONE])]
    private string $penaltyMode = self::MODE_NONE;

    /** درصد ۰..۱۰۰ یا مبلغ ریالی، بسته به `penaltyMode`. */
    #[ORM\Column(name: 'penalty_value', type: 'integer', options: ['default' => 0])]
    private int $penaltyValue = 0;

    #[ORM\Column(name: 'deposit_refundable', type: 'boolean', options: ['default' => false])]
    private bool $depositRefundable = false;

    #[ORM\Column(name: 'credit_refundable', type: 'boolean', options: ['default' => true])]
    private bool $creditRefundable = true;

    #[ORM\Column(name: 'no_show_threshold', type: 'smallint', options: ['default' => 3])]
    private int $noShowThreshold = 3;

    /** بدون FK — همان الگوی `DiscountRule.target_tag_uuid` موجود پروژه. */
    #[ORM\Column(name: 'risk_tag_uuid', type: 'string', length: 36, nullable: true)]
    private ?string $riskTagUuid = null;

    #[ORM\Column(type: 'boolean', options: ['default' => true])]
    private bool $active = true;

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

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

    public function __construct(string $entityType, int $entityId, ?ServiceItem $serviceItem = null)
    {
        $this->uuid        = Uuid::v4()->toRfc4122();
        $this->serviceItem = $serviceItem;
        $this->createdAt   = time();
        $this->updatedAt   = time();

        $this->assignTenantPair($entityType, $entityId);
    }

    public function getId(): ?int                  { return $this->id; }
    public function getUuid(): string              { return $this->uuid; }
    public function getServiceItem(): ?ServiceItem { return $this->serviceItem; }
    public function getFreeWindowHours(): int      { return $this->freeWindowHours; }
    public function getPenaltyMode(): string       { return $this->penaltyMode; }
    public function getPenaltyValue(): int         { return $this->penaltyValue; }
    public function isDepositRefundable(): bool    { return $this->depositRefundable; }
    public function isCreditRefundable(): bool     { return $this->creditRefundable; }
    public function getNoShowThreshold(): int      { return $this->noShowThreshold; }
    public function getRiskTagUuid(): ?string      { return $this->riskTagUuid; }
    public function isActive(): bool               { return $this->active; }

    public function setFreeWindowHours(int $v): self   { $this->freeWindowHours = max(0, $v); return $this->touch(); }
    public function setDepositRefundable(bool $v): self { $this->depositRefundable = $v; return $this->touch(); }
    public function setCreditRefundable(bool $v): self  { $this->creditRefundable = $v; return $this->touch(); }
    public function setRiskTagUuid(?string $v): self    { $this->riskTagUuid = $v; return $this->touch(); }
    public function setActive(bool $v): self            { $this->active = $v; return $this->touch(); }

    public function setNoShowThreshold(int $v): self
    {
        // آستانهٔ صفر یعنی هر بیماری از همان نوبت اول پرریسک است.
        $this->noShowThreshold = max(1, $v);

        return $this->touch();
    }

    /** @throws \InvalidArgumentException روی حالت ناشناخته یا درصد بیرون بازه */
    public function setPenalty(string $mode, int $value): self
    {
        if (!in_array($mode, self::MODES, true)) {
            throw new \InvalidArgumentException(sprintf('Unknown penalty mode "%s".', $mode));
        }

        if ($mode === self::MODE_PERCENT && ($value < 0 || $value > 100)) {
            throw new \InvalidArgumentException('A percentage penalty must be between 0 and 100.');
        }

        $this->penaltyMode  = $mode;
        $this->penaltyValue = $mode === self::MODE_NONE ? 0 : max(0, $value);

        return $this->touch();
    }

    private function touch(): self
    {
        $this->updatedAt = time();

        return $this;
    }

    /** @return array<string, mixed> */
    public function toArray(): array
    {
        return [
            'uuid'               => $this->uuid,
            'service_uuid'       => $this->serviceItem?->getUuid(),
            'service_name'       => $this->serviceItem?->getName(),
            'free_window_hours'  => $this->freeWindowHours,
            'penalty_mode'       => $this->penaltyMode,
            'penalty_value'      => $this->penaltyValue,
            'deposit_refundable' => $this->depositRefundable,
            'credit_refundable'  => $this->creditRefundable,
            'no_show_threshold'  => $this->noShowThreshold,
            'risk_tag_uuid'      => $this->riskTagUuid,
            'active'             => $this->active,
            'created_at'         => $this->createdAt,
        ];
    }
}
