<?php

declare(strict_types=1);

namespace App\Domain\Blog;

final class AccessDecision
{
    public const ALLOW = 'allow';

    public const NEED_LOGIN = 'need_login';

    public const NEED_PURCHASE = 'need_purchase';

    public const NEED_PASSWORD = 'need_password';

    /** @var array<string, int> */
    private const SEVERITY = [
        self::ALLOW => 0,
        self::NEED_LOGIN => 1,
        self::NEED_PURCHASE => 2,
        self::NEED_PASSWORD => 3,
    ];

    public function __construct(
        public string $status,
        public ?string $teaserHtml = null,
        public ?string $checkoutUrl = null,
        public ?string $message = null,
    ) {}

    public static function allow(): self
    {
        return new self(self::ALLOW);
    }

    public static function needPassword(?string $message = null): self
    {
        return new self(self::NEED_PASSWORD, message: $message);
    }

    public static function needPurchase(?string $teaserHtml = null, ?string $checkoutUrl = null, ?string $message = null): self
    {
        return new self(self::NEED_PURCHASE, $teaserHtml, $checkoutUrl, $message);
    }

    public function isAllow(): bool
    {
        return $this->status === self::ALLOW;
    }

    public function severity(): int
    {
        return self::SEVERITY[$this->status] ?? 0;
    }

    /**
     * Only allow tightening (higher severity). Metadata from the stricter side wins when status changes;
     * otherwise fill empty meta from $other.
     */
    public function tightenWith(self $other): self
    {
        if ($other->severity() < $this->severity()) {
            return new self(
                $this->status,
                $this->teaserHtml ?? $other->teaserHtml,
                $this->checkoutUrl ?? $other->checkoutUrl,
                $this->message ?? $other->message,
            );
        }

        if ($other->severity() > $this->severity()) {
            return new self(
                $other->status,
                $other->teaserHtml ?? $this->teaserHtml,
                $other->checkoutUrl ?? $this->checkoutUrl,
                $other->message ?? $this->message,
            );
        }

        return new self(
            $this->status,
            $other->teaserHtml ?? $this->teaserHtml,
            $other->checkoutUrl ?? $this->checkoutUrl,
            $other->message ?? $this->message,
        );
    }

    /**
     * @return array{status: string, teaser_html: ?string, checkout_url: ?string, message: ?string}
     */
    public function toArray(): array
    {
        return [
            'status' => $this->status,
            'teaser_html' => $this->teaserHtml,
            'checkout_url' => $this->checkoutUrl,
            'message' => $this->message,
        ];
    }
}
