<?php

namespace Fotogenic_AI;

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

/**
 * Single authority for binding durable runs, admitted outputs and attachments.
 */
final class Generate_Result_Authority {
	/** @return array<string,int> */
	public static function admitted_attachment_map( array $row ): array {
		$workflow = sanitize_key( (string) ( $row['workflow'] ?? '' ) );
		$job_ids  = self::normalize_job_ids( $row['job_ids'] ?? [] );
		$map      = Generate_Output_Map::normalize( $row['attachments_map'] ?? [], $job_ids, $workflow );
		if ( empty( $job_ids ) || empty( $map ) ) {
			return [];
		}

		if ( 'campaign_set' !== $workflow ) {
			return $map;
		}

		$campaign_outputs = self::campaign_outputs_by_key( $row, $job_ids );
		if ( empty( $campaign_outputs ) ) {
			return [];
		}
		foreach ( array_keys( $map ) as $map_key ) {
			if ( ! isset( $campaign_outputs[ $map_key ] ) ) {
				return [];
			}
		}

		return $map;
	}

	/** @return array<string,int> */
	public static function validated_attachment_map( array $row ): array {
		$workflow = sanitize_key( (string) ( $row['workflow'] ?? '' ) );
		$job_ids  = self::normalize_job_ids( $row['job_ids'] ?? [] );
		$map      = self::admitted_attachment_map( $row );
		if ( empty( $map ) ) {
			return [];
		}

		$campaign_outputs = 'campaign_set' === $workflow
			? self::campaign_outputs_by_key( $row, $job_ids )
			: [];
		$validated = [];
		foreach ( $map as $map_key => $attachment_id ) {
			if ( ! get_post( (int) $attachment_id ) ) {
				continue;
			}
			$job_id = Generate_Output_Map::job_id_for_key( (string) $map_key, $job_ids );
			$output = 'campaign_set' === $workflow ? ( $campaign_outputs[ $map_key ] ?? null ) : null;
			if ( '' === $job_id || ! self::attachment_matches_row( (int) $attachment_id, $row, $job_id, $output ) ) {
				return [];
			}
			$validated[ $map_key ] = $attachment_id;
		}

		return $validated;
	}

	public static function attachment_matches_context( int $attachment_id, array $context ): bool {
		$row = [
			'user_id'       => $context['user_id'] ?? 0,
			'workflow'      => $context['workflow'] ?? '',
			'run_id'        => $context['run_id'] ?? '',
			'request_id'    => $context['request_id'] ?? '',
			'run_target'    => $context['run_target'] ?? '',
			'subject_id'    => $context['subject_id'] ?? 0,
			'run_item_uuid' => $context['run_item_uuid'] ?? '',
		];
		$workflow = sanitize_key( (string) $row['workflow'] );
		$output = null;
		if ( 'campaign_set' === $workflow ) {
			$asset_id    = Upstream_Identifier::normalize( $context['asset_id'] ?? null );
			$asset_order = $context['asset_order'] ?? null;
			$admission   = Generate_Campaign_Admission::normalize( $context['campaign_admission'] ?? null );
			if ( '' === $asset_id || ! is_int( $asset_order ) || empty( $admission ) ) {
				return false;
			}
			$output = $admission['outputs'][ $asset_order ] ?? null;
			if (
				! is_array( $output )
				|| ! hash_equals( (string) $output['output_id'], $asset_id )
				|| ! hash_equals( (string) $output['job_id'], (string) ( $context['job_id'] ?? '' ) )
			) {
				return false;
			}
		}

		return self::attachment_matches_row( $attachment_id, $row, (string) ( $context['job_id'] ?? '' ), $output );
	}

	/**
	 * @param array<string,mixed>|null $campaign_output Exact admitted Campaign output.
	 */
	public static function attachment_matches_row( int $attachment_id, array $row, string $job_id, ?array $campaign_output = null ): bool {
		$user_id       = absint( $row['user_id'] ?? 0 );
		$workflow      = sanitize_key( (string) ( $row['workflow'] ?? '' ) );
		$run_id        = trim( (string) ( $row['run_id'] ?? '' ) );
		$request_id    = strtolower( trim( (string) ( $row['request_id'] ?? '' ) ) );
		$run_target    = sanitize_key( (string) ( $row['run_target'] ?? '' ) );
		$subject_id    = absint( $row['subject_id'] ?? 0 );
		$run_item_uuid = strtolower( trim( (string) ( $row['run_item_uuid'] ?? '' ) ) );
		$job_id        = Upstream_Identifier::normalize( $job_id );
		if (
			$attachment_id <= 0
			|| ! get_post( $attachment_id )
			|| ! wp_attachment_is_image( $attachment_id )
			|| $user_id <= 0
			|| ! in_array( $workflow, [ 'product_images', 'campaign_set', 'increase_resolution' ], true )
			|| '' === $run_id
			|| ! wp_is_uuid( $request_id )
			|| ! in_array( $run_target, [ 'product', 'media', 'post', 'page' ], true )
			|| $subject_id <= 0
			|| ! wp_is_uuid( $run_item_uuid )
			|| '' === $job_id
		) {
			return false;
		}

		if (
			1 !== (int) get_post_meta( $attachment_id, '_fotogenic_ai_generated', true )
			|| $user_id !== (int) get_post_meta( $attachment_id, '_fotogenic_ai_user_id', true )
			|| ! hash_equals( $workflow, sanitize_key( (string) get_post_meta( $attachment_id, '_fotogenic_ai_workflow', true ) ) )
			|| ! hash_equals( $run_id, (string) get_post_meta( $attachment_id, '_fotogenic_ai_run_id', true ) )
			|| ! hash_equals( $request_id, strtolower( (string) get_post_meta( $attachment_id, '_fotogenic_ai_request_id', true ) ) )
			|| ! hash_equals( $run_target, sanitize_key( (string) get_post_meta( $attachment_id, '_fotogenic_ai_run_target', true ) ) )
			|| $subject_id !== (int) get_post_meta( $attachment_id, '_fotogenic_ai_subject_id', true )
			|| ! hash_equals( $run_item_uuid, strtolower( (string) get_post_meta( $attachment_id, '_fotogenic_ai_run_item_uuid', true ) ) )
			|| ! hash_equals( $job_id, (string) get_post_meta( $attachment_id, '_fotogenic_ai_job_id', true ) )
		) {
			return false;
		}

		if ( 'campaign_set' !== $workflow ) {
			return null === $campaign_output;
		}
		if ( ! is_array( $campaign_output ) || ! metadata_exists( 'post', $attachment_id, '_fotogenic_ai_asset_order' ) ) {
			return false;
		}

		return hash_equals( (string) $campaign_output['output_id'], (string) get_post_meta( $attachment_id, '_fotogenic_ai_asset_id', true ) )
			&& (int) $campaign_output['output_index'] === (int) get_post_meta( $attachment_id, '_fotogenic_ai_asset_order', true )
			&& hash_equals( (string) $campaign_output['job_id'], $job_id );
	}

	/** @return string[] */
	private static function normalize_job_ids( $value ): array {
		if ( is_string( $value ) ) {
			$decoded = json_decode( $value, true );
			$value   = is_array( $decoded ) ? $decoded : [ $value ];
		}
		if ( ! is_array( $value ) ) {
			return [];
		}
		$job_ids = [];
		foreach ( $value as $job_id ) {
			$job_id = Upstream_Identifier::normalize( $job_id );
			if ( '' !== $job_id && ! in_array( $job_id, $job_ids, true ) ) {
				$job_ids[] = $job_id;
			}
		}
		return $job_ids;
	}

	/** @return array<string,array{output_id:string,output_index:int,job_id:string,status:string}> */
	private static function campaign_outputs_by_key( array $row, array $job_ids ): array {
		$admission = Generate_Campaign_Admission::from_finalize_meta( $row['finalize_meta'] ?? null );
		if ( empty( $admission ) ) {
			return [];
		}

		$outputs = [];
		foreach ( $admission['outputs'] as $output ) {
			$job_id = (string) $output['job_id'];
			if ( ! in_array( $job_id, $job_ids, true ) ) {
				return [];
			}
			$key = Generate_Output_Map::campaign_key( $job_id, (string) $output['output_id'], (int) $output['output_index'] );
			if ( '' === $key || isset( $outputs[ $key ] ) ) {
				return [];
			}
			$outputs[ $key ] = $output;
		}

		return $outputs;
	}
}
