<?php

declare(strict_types=1);

namespace FormConnector\Integration\AI;

/**
 * Response Parser for AI mapping suggestions.
 *
 * Strictly validates the JSON returned by the AI model and normalises
 * it into a clean suggestions array. Falls back gracefully on garbage
 * input — never throws.
 */
class ResponseParser {

	/**
	 * Parse and validate the AI model's response text.
	 *
	 * @param string $raw_text       The raw text from the model
	 * @param array  $form_field_ids Valid form field IDs (used to filter)
	 * @param array  $carehq_keys    Valid CareHQ field keys (used to filter)
	 * @return array Associative array of field_id => ['target' => string, 'confidence' => float, 'reason' => string]
	 */
	public static function parse(string $raw_text, array $form_field_ids, array $carehq_keys): array {
		$json = self::extract_json($raw_text);

		if ($json === null) {
			return [];
		}

		return self::validate_suggestions($json, $form_field_ids, $carehq_keys);
	}

	/**
	 * Validate and filter a pre-parsed suggestions array.
	 * Used when the backend server returns already-parsed suggestions.
	 *
	 * @param array $suggestions    Suggestions array (possibly from backend)
	 * @param array $form_field_ids Valid form field IDs
	 * @param array $carehq_keys    Valid CareHQ field keys
	 * @return array Validated suggestions
	 */
	public static function parse_array(array $suggestions, array $form_field_ids, array $carehq_keys): array {
		return self::validate_suggestions($suggestions, $form_field_ids, $carehq_keys);
	}

	/**
	 * Validate and normalise suggestions against known fields.
	 *
	 * @param array $json           Raw suggestions data
	 * @param array $form_field_ids Valid form field IDs
	 * @param array $carehq_keys    Valid CareHQ field keys
	 * @return array Validated suggestions
	 */
	private static function validate_suggestions(array $json, array $form_field_ids, array $carehq_keys): array {

		$suggestions = [];
		$used_targets = [];

		foreach ($json as $field_id => $entry) {
			// Skip unknown form fields
			if (!in_array((string) $field_id, $form_field_ids, true)) {
				continue;
			}

			if (!is_array($entry)) {
				continue;
			}

			$target     = isset($entry['target']) ? (string) $entry['target'] : '';
			$confidence = isset($entry['confidence']) ? (float) $entry['confidence'] : 0.0;
			$reason     = isset($entry['reason']) ? (string) $entry['reason'] : '';

			// Clamp confidence
			$confidence = max(0.0, min(1.0, $confidence));

			// Validate target exists in CareHQ fields
			if ($target !== '' && !in_array($target, $carehq_keys, true)) {
				$target     = '';
				$confidence = 0.0;
			}

			// Prevent duplicate target assignments (first match wins)
			if ($target !== '' && isset($used_targets[$target])) {
				$target     = '';
				$confidence = 0.0;
				$reason     = 'Duplicate target — another field matched better.';
			}

			if ($target !== '') {
				$used_targets[$target] = true;
			}

			$suggestions[(string) $field_id] = [
				'target'     => $target,
				'confidence' => round($confidence, 2),
				'reason'     => mb_substr(sanitize_text_field($reason), 0, 200),
			];
		}

		return $suggestions;
	}

	/**
	 * Extract a JSON object from possibly noisy model output.
	 * Handles markdown code fences and leading/trailing junk.
	 *
	 * @param string $text
	 * @return array|null
	 */
	private static function extract_json(string $text): ?array {
		$text = trim($text);

		// Strip markdown code fences
		if (preg_match('/```(?:json)?\s*([\s\S]*?)```/', $text, $matches)) {
			$text = trim($matches[1]);
		}

		// Try direct parse first
		$decoded = json_decode($text, true);
		if (is_array($decoded) && !array_is_list($decoded)) {
			return $decoded;
		}

		// Try to find the first { ... } block
		$start = strpos($text, '{');
		$end   = strrpos($text, '}');

		if ($start !== false && $end !== false && $end > $start) {
			$json_str = substr($text, $start, $end - $start + 1);
			$decoded  = json_decode($json_str, true);

			if (is_array($decoded) && !array_is_list($decoded)) {
				return $decoded;
			}
		}

		return null;
	}
}
