<?php
/**
 * Link Memory.
 *
 * A small, shared, persistent record of internal links that Link Architect has
 * built: which source post links to which target post. The Link Suggester
 * consults it to avoid re-suggesting a link the Architect already created,
 * even after edits — so the two modules behave like one connected system
 * rather than two tools that don't know about each other.
 *
 * Storage model: one post-meta entry per source post,
 * `_platform_helm_built_links`, holding an array of target post IDs. Keyed by
 * source post so lookups during suggestion are a single get_post_meta call,
 * and so the record travels with the post (and is cleaned up automatically if
 * the post is deleted).
 *
 * This lives in Core, not inside either module, so neither module has to
 * depend on the other — both just use this shared utility.
 *
 * @package Platform\Helm\Core
 */

declare( strict_types = 1 );

namespace Platform\Helm\Core;

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

final class LinkMemory {

	/** Post-meta key on the SOURCE post; value is an array of target post IDs. */
	private const META_KEY = '_platform_helm_built_links';

	/**
	 * Record that $source_id now links to $target_id. Idempotent — recording
	 * the same pair twice is a no-op.
	 */
	public function record( int $source_id, int $target_id ): void {
		if ( $source_id <= 0 || $target_id <= 0 || $source_id === $target_id ) {
			return;
		}
		$targets = $this->targets_for( $source_id );
		if ( in_array( $target_id, $targets, true ) ) {
			return;
		}
		$targets[] = $target_id;
		update_post_meta( $source_id, self::META_KEY, array_values( array_unique( $targets ) ) );
	}

	/**
	 * Whether $source_id is already recorded as linking to $target_id.
	 */
	public function has_link( int $source_id, int $target_id ): bool {
		if ( $source_id <= 0 || $target_id <= 0 ) {
			return false;
		}
		return in_array( $target_id, $this->targets_for( $source_id ), true );
	}

	/**
	 * All target post IDs recorded for a given source post.
	 *
	 * @return int[]
	 */
	public function targets_for( int $source_id ): array {
		$stored = get_post_meta( $source_id, self::META_KEY, true );
		if ( ! is_array( $stored ) ) {
			return [];
		}
		return array_values( array_filter( array_map( 'intval', $stored ) ) );
	}

	/**
	 * Forget a single recorded pair (e.g. after a restore that removed the
	 * link). Safe to call on pairs that were never recorded.
	 */
	public function forget( int $source_id, int $target_id ): void {
		$targets = $this->targets_for( $source_id );
		$targets = array_values( array_filter( $targets, static fn( $id ) => $id !== $target_id ) );
		if ( empty( $targets ) ) {
			delete_post_meta( $source_id, self::META_KEY );
		} else {
			update_post_meta( $source_id, self::META_KEY, $targets );
		}
	}

	/**
	 * Forget every recorded link originating from a source post (e.g. a full
	 * restore of that post).
	 */
	public function forget_all_for( int $source_id ): void {
		delete_post_meta( $source_id, self::META_KEY );
	}
}
