<?php

declare(strict_types=1);

namespace App\Support\Ui\ActionSurface;

use App\Support\Ui\ActionSurface\Enums\ActionSurfaceComponentType;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;

final class ActionSurfaceDeclaration
{
    /**
     * @var array<string, ActionSurfaceSlotRequirement>
     */
    private array $slots = [];

    /**
     * @var array<string, ActionSurfaceExemption>
     */
    private array $exemptions = [];

    public ActionSurfaceDefaults $defaults;

    public function __construct(
        public readonly int $version,
        public readonly ActionSurfaceComponentType $componentType,
        public readonly ActionSurfaceProfile $profile,
        ?ActionSurfaceDefaults $defaults = null,
    ) {
        $this->defaults = $defaults ?? new ActionSurfaceDefaults;
    }

    public static function make(
        ActionSurfaceComponentType $componentType,
        ActionSurfaceProfile $profile,
        int $version = 1,
    ): self {
        return new self(
            version: $version,
            componentType: $componentType,
            profile: $profile,
        );
    }

    public static function forResource(ActionSurfaceProfile $profile, int $version = 1): self
    {
        return self::make(ActionSurfaceComponentType::Resource, $profile, $version);
    }

    public static function forPage(ActionSurfaceProfile $profile, int $version = 1): self
    {
        return self::make(ActionSurfaceComponentType::Page, $profile, $version);
    }

    public static function forRelationManager(ActionSurfaceProfile $profile, int $version = 1): self
    {
        return self::make(ActionSurfaceComponentType::RelationManager, $profile, $version);
    }

    public function withDefaults(ActionSurfaceDefaults $defaults): self
    {
        $this->defaults = $defaults;

        return $this;
    }

    public function setSlot(ActionSurfaceSlot $slot, ActionSurfaceSlotRequirement $requirement): self
    {
        $this->slots[$slot->value] = $requirement;

        return $this;
    }

    public function satisfy(
        ActionSurfaceSlot $slot,
        ?string $details = null,
        bool $requiresTypedConfirmation = false,
    ): self {
        return $this->setSlot($slot, ActionSurfaceSlotRequirement::satisfied($details, $requiresTypedConfirmation));
    }

    public function exempt(
        ActionSurfaceSlot $slot,
        string $reason,
        ?string $trackingRef = null,
        ?string $details = null,
    ): self {
        $this->setSlot($slot, ActionSurfaceSlotRequirement::exempt($details));
        $this->exemptions[$slot->value] = new ActionSurfaceExemption($slot, $reason, $trackingRef);

        return $this;
    }

    public function slot(ActionSurfaceSlot $slot): ?ActionSurfaceSlotRequirement
    {
        return $this->slots[$slot->value] ?? null;
    }

    public function exemption(ActionSurfaceSlot $slot): ?ActionSurfaceExemption
    {
        return $this->exemptions[$slot->value] ?? null;
    }

    /**
     * @return array<string, ActionSurfaceSlotRequirement>
     */
    public function slots(): array
    {
        return $this->slots;
    }

    /**
     * @return array<string, ActionSurfaceExemption>
     */
    public function exemptions(): array
    {
        return $this->exemptions;
    }
}
