<?php
/**
 * Existing-post opportunity scanner.
 *
 * @package Affilun\Application\Migration
 */

declare(strict_types=1);

namespace Affilun\Application\Migration;

/**
 * Heuristic AI-scan foundation for existing-post product-link opportunities.
 */
final class ExistingPostOpportunityScanner {

	/**
	 * Japanese review/comparison intent tokens.
	 *
	 * @var list<string>
	 */
	private const INTENT_TOKENS = array(
		'レビュー',
		'比較',
		'おすすめ',
		'ランキング',
		'選び方',
		'買って',
		'使って',
		'便利',
	);

	/**
	 * Scan one post for candidate search keywords.
	 *
	 * @param PostScanItem $post Post.
	 *
	 * @return list<array{post_id:int,post_title:string,candidate:string,reason:string,confidence:int,requires_confirmation:bool}>
	 */
	public function scanPost( PostScanItem $post ): array {
		$text = $this->plainText( $post->title() . "\n" . $post->content() );
		if ( trim( $text ) === '' ) {
			return array();
		}

		$score   = $this->intentScore( $text );
		$matches = array();
		foreach ( $this->candidateKeywords( $post->title(), $text ) as $candidate ) {
			$matches[] = array(
				'post_id'               => $post->id(),
				'post_title'            => $post->title(),
				'candidate'             => $candidate,
				'reason'                => $score > 0 ? 'review_intent' : 'title_candidate',
				'confidence'            => max( 40, min( 95, 55 + ( $score * 10 ) ) ),
				'requires_confirmation' => true,
			);
		}

		return $matches;
	}

	/**
	 * Scan several posts.
	 *
	 * @param list<PostScanItem> $posts Posts.
	 *
	 * @return array{posts_scanned:int,opportunities:list<array{post_id:int,post_title:string,candidate:string,reason:string,confidence:int,requires_confirmation:bool}>}
	 */
	public function scanBatch( array $posts ): array {
		$opportunities = array();
		foreach ( $posts as $post ) {
			$opportunities = array_merge( $opportunities, $this->scanPost( $post ) );
		}

		return array(
			'posts_scanned' => count( $posts ),
			'opportunities' => $opportunities,
		);
	}

	/**
	 * Score product-link opportunity intent.
	 *
	 * @param string $text Plain text.
	 */
	private function intentScore( string $text ): int {
		$score = 0;
		foreach ( self::INTENT_TOKENS as $token ) {
			if ( str_contains( $text, $token ) ) {
				++$score;
			}
		}
		$marketplacePattern = '/https?:\/\/[^\s]*(?:\/dp\/[A-Z0-9]{8,}'
			. '|item\.rakuten\.co\.jp|shopping\.yahoo\.co\.jp)/i';
		if ( preg_match( $marketplacePattern, $text ) === 1 ) {
			$score += 2;
		}
		return $score;
	}

	/**
	 * Build candidate keywords from title and text.
	 *
	 * @param string $title Title.
	 * @param string $text  Plain text.
	 *
	 * @return list<string>
	 */
	private function candidateKeywords( string $title, string $text ): array {
		$candidates = array();
		$cleanTitle = $this->normalizeCandidate( $title );
		if ( $cleanTitle !== '' ) {
			$candidates[] = $cleanTitle;
		}

		if ( preg_match_all( '/[A-Za-z0-9][A-Za-z0-9\- ]{2,40}/', $text, $found ) === 1 ) {
			foreach ( $found[0] as $candidate ) {
				$normalized = $this->normalizeCandidate( $candidate );
				if ( $normalized !== '' ) {
					$candidates[] = $normalized;
				}
			}
		}

		$unique = array();
		foreach ( $candidates as $candidate ) {
			$key = strtolower( $candidate );
			if ( isset( $unique[ $key ] ) ) {
				continue;
			}
			$unique[ $key ] = $candidate;
			if ( count( $unique ) >= 3 ) {
				break;
			}
		}

		return array_values( $unique );
	}

	/**
	 * Normalize candidate text.
	 *
	 * @param string $value Candidate.
	 */
	private function normalizeCandidate( string $value ): string {
		$value = trim( preg_replace( '/\s+/', ' ', $this->plainText( $value ) ) ?? $value );
		$value = trim( preg_replace( '/(?:レビュー|比較|おすすめ|ランキング|選び方)$/u', '', $value ) ?? $value );
		if ( $value === '' ) {
			return '';
		}
		if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_substr' ) ) {
			return mb_strlen( $value ) > 60 ? mb_substr( $value, 0, 60 ) : $value;
		}
		return strlen( $value ) > 60 ? substr( $value, 0, 60 ) : $value;
	}

	/**
	 * Convert HTML/block content to plain text.
	 *
	 * @param string $content Content.
	 */
	private function plainText( string $content ): string {
		if ( function_exists( 'wp_strip_all_tags' ) ) {
			return (string) wp_strip_all_tags( $content );
		}
		return trim( preg_replace( '/<[^>]*>/', '', $content ) ?? $content );
	}
}
