<?php
/**
 * Detects unresolved internal @id-only references.
 *
 * @package SEOvaultSEO\Schema\Validation\Rules\Core
 */

namespace SEOvaultSEO\Schema\Validation\Rules\Core;

use SEOvaultSEO\Schema\Validation\RuleInterface;
use SEOvaultSEO\Schema\Validation\Severity;
use SEOvaultSEO\Schema\Validation\ValidationContext;
use SEOvaultSEO\Schema\Validation\ValidationIssue;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Validates local graph references without attempting to resolve remote URLs.
 */
class BrokenIdReferenceRule implements RuleInterface {

	/**
	 * Get the stable rule code.
	 *
	 * @return string
	 */
	public function get_code() {
		return 'graph.reference.unresolved';
	}

	/**
	 * Reference checks apply to every graph.
	 *
	 * @param ValidationContext $context Validation context.
	 * @return bool
	 */
	public function supports( ValidationContext $context ) {
		return true;
	}

	/**
	 * Detect unresolved fragment and relative references.
	 *
	 * @param ValidationContext $context Validation context.
	 * @return ValidationIssue[]
	 */
	public function evaluate( ValidationContext $context ) {
		$graph      = $context->get_graph();
		$references = $graph['references'] ?? array();
		$known_ids  = array();
		$issues     = array();

		foreach ( $context->get_entities() as $entity ) {
			$id = $entity['id'] ?? '';
			if ( is_string( $id ) && '' !== $id ) {
				$known_ids[ $id ] = true;
			}
		}

		foreach ( $references as $reference ) {
			$id = $reference['id'] ?? '';
			if ( ! is_string( $id ) || '' === $id || ! $this->is_internal_reference( $id ) || isset( $known_ids[ $id ] ) ) {
				continue;
			}

			$issues[] = new ValidationIssue(
				array(
					'code'        => 'graph.reference.unresolved',
					'severity'    => Severity::ERROR,
					'category'    => 'core',
					'entity_id'   => $id,
					'path'        => $reference['path'] ?? '',
					'source'      => $context->get_source(),
					'title_key'   => 'graph.reference_unresolved.title',
					'message_key' => 'graph.reference_unresolved.message',
				)
			);
		}

		return $issues;
	}

	/**
	 * Determine whether an identifier refers to the local graph.
	 *
	 * @param string $id Identifier value.
	 * @return bool
	 */
	private function is_internal_reference( $id ) {
		if ( '#' === substr( $id, 0, 1 ) ) {
			return true;
		}

		$parts = wp_parse_url( $id );
		return is_array( $parts ) && ! isset( $parts['scheme'], $parts['host'] );
	}
}
