<?php

declare(strict_types=1);

namespace App\Module\Core\Domain\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use App\Module\Core\Infrastructure\Doctrine\DoctrinePermissionRepository;
use App\Shared\Domain\Attribute\Auditable;
use Doctrine\ORM\Mapping as ORM;
use InvalidArgumentException;
use Symfony\Component\Serializer\Attribute\Groups;

#[Auditable]
#[ORM\Entity(repositoryClass: DoctrinePermissionRepository::class)]
#[ORM\Table(name: 'permission')]
#[ORM\Index(name: 'idx_permission_module', columns: ['module'])]
#[ORM\Index(name: 'idx_permission_orphan', columns: ['orphan'])]
#[ApiResource(
    operations: [
        new GetCollection(paginationEnabled: false),
        new Get(),
    ],
    normalizationContext: ['groups' => ['permission:read']],
    security: "is_granted('core.permissions.view') or is_granted('core.users.manage') or is_granted('core.roles.manage')",
)]
class Permission
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    #[Groups(['permission:read', 'role:read'])]
    private ?int $id = null;

    #[ORM\Column(length: 255, unique: true, options: ['comment' => 'Permission code (module.resource[.sub].action)'])]
    #[Groups(['permission:read', 'role:read'])]
    private string $code;

    #[ORM\Column(length: 255, options: ['comment' => 'Human-readable permission label'])]
    #[Groups(['permission:read', 'role:read'])]
    private string $label;

    #[ORM\Column(length: 100, options: ['comment' => 'Owning module id (e.g. core)'])]
    #[Groups(['permission:read', 'role:read'])]
    private string $module;

    #[ORM\Column(options: ['comment' => 'True when the permission is no longer declared by any active module'])]
    #[Groups(['permission:read'])]
    private bool $orphan = false;

    public function __construct(string $code, string $label, string $module)
    {
        $code   = trim($code);
        $label  = trim($label);
        $module = trim($module);

        if ('' === $code || !str_contains($code, '.')) {
            throw new InvalidArgumentException(sprintf('Code de permission invalide : "%s" (attendu module.resource.action).', $code));
        }
        if ('' === $label) {
            throw new InvalidArgumentException('Le libellé de permission ne peut pas être vide.');
        }
        if ('' === $module) {
            throw new InvalidArgumentException('Le module de permission ne peut pas être vide.');
        }

        $this->code   = $code;
        $this->label  = $label;
        $this->module = $module;
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getCode(): string
    {
        return $this->code;
    }

    public function getLabel(): string
    {
        return $this->label;
    }

    public function getModule(): string
    {
        return $this->module;
    }

    public function isOrphan(): bool
    {
        return $this->orphan;
    }

    public function markOrphan(): void
    {
        $this->orphan = true;
    }

    public function revive(string $label, string $module): void
    {
        $this->orphan = false;
        $this->updateMetadata($label, $module);
    }

    public function updateMetadata(string $label, string $module): void
    {
        $this->label  = $label;
        $this->module = $module;
    }
}
