<?php
/**
 * Costudio_Editorial_Flags
 *
 * Scans a finished production's agent outputs for "verify before publishing"
 * markers the agents emit when something could not be grounded — `[GAP]`,
 * `[UNVERIFIED]`, `[INFERRED]`, the Researcher's "Editorial team note before
 * publishing", and "confirm URL" notes. The production detail view surfaces the
 * result as a visible checklist so a human editor confirms or removes the claim
 * before publishing (instead of those flags staying buried in raw agent output).
 *
 * Read-only/presentational: no DB writes, no external calls.
 *
 * @package Contentosapp_Studio
 * @since   1.2.3
 */

defined( 'ABSPATH' ) || exit;

final class Costudio_Editorial_Flags {

	private const MAX_ITEMS = 20;
	private const MAX_LEN    = 200;

	/**
	 * Extract review flags from a production's agent outputs.
	 *
	 * @param object $production Production object exposing ->output_03 (Researcher)
	 *                           and ->output_04 (Writer/final article) as nullable strings.
	 * @return array<int,array{type:string,label:string,text:string,agent:int}>
	 */
	public static function extract( $production ): array {
		$out03 = is_object( $production ) && isset( $production->output_03 ) ? (string) $production->output_03 : '';
		$out04 = is_object( $production ) && isset( $production->output_04 ) ? (string) $production->output_04 : '';

		$flags = array();
		self::scan( $out03, 3, $flags );
		self::scan( $out04, 4, $flags );

		// Dedupe by type+text; cap the list.
		$seen   = array();
		$unique = array();
		foreach ( $flags as $flag ) {
			$key = mb_strtolower( $flag['type'] . '|' . $flag['text'] );
			if ( '' === $flag['text'] || isset( $seen[ $key ] ) ) {
				continue;
			}
			$seen[ $key ] = true;
			$unique[]     = $flag;
			if ( count( $unique ) >= self::MAX_ITEMS ) {
				break;
			}
		}

		return $unique;
	}

	/**
	 * @param array<int,array{type:string,label:string,text:string,agent:int}> $flags
	 */
	private static function scan( string $text, int $agent, array &$flags ): void {
		if ( '' === trim( $text ) ) {
			return;
		}

		$lines = preg_split( '/\r\n|\r|\n/', $text );
		if ( ! is_array( $lines ) ) {
			return;
		}

		foreach ( $lines as $line ) {
			$type = self::classify( $line );
			if ( null === $type ) {
				continue;
			}
			$clean = self::clean_line( $line );
			if ( '' === $clean ) {
				continue;
			}
			$flags[] = array(
				'type'  => $type,
				'label' => self::label_for( $type ),
				'text'  => self::truncate( $clean ),
				'agent' => $agent,
			);
		}
	}

	private static function classify( string $line ): ?string {
		if ( preg_match( '/\[UNVERIFIED\]/i', $line ) ) {
			return 'unverified';
		}
		if ( preg_match( '/\[GAP\b[^\]]*\]/i', $line ) ) {
			return 'gap';
		}
		if ( preg_match( '/\[INFERRED\]/i', $line ) ) {
			return 'inferred';
		}
		if ( false !== stripos( $line, 'Editorial team note' ) ) {
			return 'editorial_note';
		}
		if ( preg_match( '/confirm (the exact |deep )?url/i', $line )
			|| false !== stripos( $line, 'to confirm at runtime' )
			|| false !== stripos( $line, 'to be confirmed editorially' ) ) {
			return 'confirm';
		}
		return null;
	}

	private static function label_for( string $type ): string {
		switch ( $type ) {
			case 'gap':
				return __( 'Source to confirm', 'contentosapp-studio' );
			case 'unverified':
				return __( 'Unverified claim', 'contentosapp-studio' );
			case 'inferred':
				return __( 'Inferred', 'contentosapp-studio' );
			case 'editorial_note':
				return __( 'Researcher note', 'contentosapp-studio' );
			case 'confirm':
				return __( 'Confirm URL', 'contentosapp-studio' );
			default:
				return __( 'Review', 'contentosapp-studio' );
		}
	}

	private static function clean_line( string $line ): string {
		$line = wp_strip_all_tags( $line );
		// Strip leading markdown / table noise (bullets, quotes, headings, pipes).
		$line = preg_replace( '/^[\s>#\-\*\|]+/', '', (string) $line );
		$line = preg_replace( '/\s+/', ' ', (string) $line );
		return trim( (string) $line, " \t|*`" );
	}

	private static function truncate( string $text ): string {
		if ( mb_strlen( $text ) <= self::MAX_LEN ) {
			return $text;
		}
		return rtrim( mb_substr( $text, 0, self::MAX_LEN ) ) . '…';
	}
}
