<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Integrations\Structural\Core;

use Ekesto\CacheInvalidator\ContentVisibility;

/**
 * Value object describing one provider-owned structural resolution step.
 */
final class StructuralContainerResolution
{
    /**
     * @var int[]
     */
    private array $directTargetIds;

    /**
     * @var StructuralContainerReference[]
     */
    private array $structuralReferences;

    private bool $hasReferences;

    private bool $uncertainDueToContext;

    private bool $uncertainDueToScanGap;

    /**
     * @var string[]
     */
    private array $directTargetUrls;

    private string $fallbackReasonHint;

    /**
     * Build a structural resolution step result.
     *
     * @param int[]                          $directTargetIds Direct precise targets.
     * @param StructuralContainerReference[] $structuralReferences Nested structural references.
     * @param bool                           $hasReferences Whether any relevant references were detected.
     * @param bool                           $uncertainDueToContext Whether context prevents safe completion.
     * @param bool                           $uncertainDueToScanGap Whether scan coverage is incomplete.
     * @param string[]                       $directTargetUrls Direct precise URL targets.
     * @param string                         $fallbackReasonHint Diagnostic fallback reason hint.
     */
    public function __construct(
        array $directTargetIds,
        array $structuralReferences,
        bool $hasReferences,
        bool $uncertainDueToContext,
        bool $uncertainDueToScanGap,
        array $directTargetUrls = [],
        string $fallbackReasonHint = ''
    ) {
        $this->directTargetIds = ContentVisibility::normalizePostIds($directTargetIds);
        $this->structuralReferences = self::normalizeStructuralReferences($structuralReferences);
        $this->directTargetUrls = ContentVisibility::normalizeUrls($directTargetUrls);
        $this->fallbackReasonHint = sanitize_key($fallbackReasonHint);
        $this->hasReferences = $hasReferences
            || !empty($this->directTargetIds)
            || !empty($this->directTargetUrls)
            || !empty($this->structuralReferences);
        $this->uncertainDueToContext = $uncertainDueToContext;
        $this->uncertainDueToScanGap = $uncertainDueToScanGap;
    }

    /**
     * Return direct precise target IDs.
     *
     * @return int[]
     */
    public function directTargetIds(): array
    {
        return $this->directTargetIds;
    }

    /**
     * Return nested structural references.
     *
     * @return StructuralContainerReference[]
     */
    public function structuralReferences(): array
    {
        return $this->structuralReferences;
    }

    /**
     * Return direct precise target URLs.
     *
     * @return string[]
     */
    public function directTargetUrls(): array
    {
        return $this->directTargetUrls;
    }

    /**
     * Return diagnostic fallback reason hint.
     *
     * @return string
     */
    public function fallbackReasonHint(): string
    {
        return $this->fallbackReasonHint;
    }

    /**
     * Return whether any references were detected.
     *
     * @return bool
     */
    public function hasReferences(): bool
    {
        return $this->hasReferences;
    }

    /**
     * Return context uncertainty.
     *
     * @return bool
     */
    public function uncertainDueToContext(): bool
    {
        return $this->uncertainDueToContext;
    }

    /**
     * Return scan-gap uncertainty.
     *
     * @return bool
     */
    public function uncertainDueToScanGap(): bool
    {
        return $this->uncertainDueToScanGap;
    }

    /**
     * Normalize structural references to unique canonical entries.
     *
     * @param StructuralContainerReference[] $references Candidate references.
     * @return StructuralContainerReference[]
     */
    private static function normalizeStructuralReferences(array $references): array
    {
        $normalized = [];

        foreach ($references as $reference) {
            if (!$reference instanceof StructuralContainerReference) {
                continue;
            }

            if ($reference->postId() <= 0 || $reference->postType() === '') {
                continue;
            }

            $normalized[$reference->canonicalKey()] = $reference;
        }

        ksort($normalized, SORT_STRING);

        return array_values($normalized);
    }
}
