<?php

namespace NoahVet\Reef\Test\A_Unit\Application\Security\Service;

use NoahVet\Reef\Application\Security\Service\PolicyOrchestrator;
use NoahVet\Reef\Domain\Security\Model\ResourceType;
use NoahVet\Reef\Domain\Security\Policy\PolicyInterface;
use NoahVet\Reef\Domain\Security\Policy\PolicyResult;
use PHPUnit\Framework\TestCase;

class PolicyOrchestratorTest extends TestCase
{
    private PolicyInterface $matchingPolicy;

    private PolicyInterface $nonMatchingPolicy;

    protected function setUp(): void
    {
        $this->matchingPolicy = new class implements PolicyInterface {
            public function getName(): string
            {
                return 'matching';
            }

            public function subjectToString(object $subject): string
            {
                return (string) $subject;
            }

            public function computeEtag(object $subject): string
            {
                return 'etag';
            }

            public function apply(object $subject): PolicyResult
            {
                return new PolicyResult($this->getName(), (string) $subject, ResourceType::fromString('reef:test'));
            }

            public function canHandle(object $subject): bool
            {
                return true;
            }
        };

        $this->nonMatchingPolicy = new class implements PolicyInterface {
            public function getName(): string
            {
                return 'non-matching';
            }

            public function subjectToString(object $subject): string
            {
                return (string) $subject;
            }

            public function computeEtag(object $subject): string
            {
                return 'etag';
            }

            public function apply(object $subject): PolicyResult
            {
                return new PolicyResult($this->getName(), (string) $subject, ResourceType::fromString('reef:test'));
            }

            public function canHandle(object $subject): bool
            {
                return false;
            }
        };
    }

    public function testGetPoliciesForReturnsMatchingPoliciesOnly(): void
    {
        $subject = new \stdClass();
        $orchestrator = new PolicyOrchestrator([$this->matchingPolicy, $this->nonMatchingPolicy]);

        $result = $orchestrator->getPoliciesFor($subject);

        self::assertCount(1, $result);
        self::assertSame($this->matchingPolicy, $result[0]);
    }

    public function testGetPoliciesForReturnsEmptyListWhenNoMatch(): void
    {
        $subject = new \stdClass();
        $orchestrator = new PolicyOrchestrator([$this->nonMatchingPolicy]);

        self::assertSame([], $orchestrator->getPoliciesFor($subject));
    }

    public function testGetPoliciesForReturnsEmptyListWithNoPolicies(): void
    {
        $orchestrator = new PolicyOrchestrator([]);

        self::assertSame([], $orchestrator->getPoliciesFor(new \stdClass()));
    }

    public function testAllReturnsAllPoliciesUnfiltered(): void
    {
        $policies = [$this->matchingPolicy, $this->nonMatchingPolicy];
        $orchestrator = new PolicyOrchestrator($policies);

        $all = [...$orchestrator->all()];

        self::assertCount(2, $all);
    }
}
