<?php

namespace Fotogenic_AI;

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

final class Service_Campaign_Set_Start {
    private const WORKFLOW = 'campaign_set';
    private const MIN_INPUTS = 1;
    private const MAX_INPUTS = 10;

    private Workflow_Start_Coordinator $coordinator;

    public function __construct( ?Workflow_Start_Coordinator $coordinator = null ) {
        $this->coordinator = $coordinator ?? new Workflow_Start_Coordinator();
    }

    /**
     * Start one Campaign Set request without routing through the REST server.
     *
     * The caller must persist its own request identity before invoking this method.
     * This command persists the workflow run and request identity before the upstream
     * billable request and never retries an uncertain start.
     *
     * @param int   $user_id User who owns the request.
     * @param array $command Command metadata and normalized browser identifiers.
     * @param array $options Internal execution options.
     * @return array|\WP_Error
     */
    public function start_command( int $user_id, array $command, array $options = [] ) {
        if ( $user_id <= 0 || get_current_user_id() !== $user_id ) {
            return Messages::error( 'forbidden', 403 );
        }

		$allowed_command_fields = [ 'target', 'subjectId', 'requestId', 'productAttachmentIds', 'recipe' ];
        if ( ! empty( array_diff( array_keys( $command ), $allowed_command_fields ) ) ) {
            return Messages::error( 'invalid_request', 400 );
        }

        $connected = $this->coordinator->ensure_connected();
        if ( is_wp_error( $connected ) ) {
            return $connected;
        }

        $request_id = $this->coordinator->validate_request_id( (string) ( $command['requestId'] ?? '' ) );
        if ( is_wp_error( $request_id ) ) {
            return $request_id;
        }

        $target = sanitize_key( (string) ( $command['target'] ?? '' ) );
        $subject_id = (int) ( $command['subjectId'] ?? 0 );
		$context = $this->coordinator->resolve_target_context( $target, $subject_id );
        if ( is_wp_error( $context ) ) {
            return $context;
        }
        $context = $this->coordinator->attach_item_identity_context( $context, (array) ( $options['item_identity'] ?? [] ) );
        if ( is_wp_error( $context ) ) {
            return $context;
        }

        $product_attachment_ids = $this->normalize_attachment_ids( $command['productAttachmentIds'] ?? null );
        if ( is_wp_error( $product_attachment_ids ) ) {
            return $product_attachment_ids;
        }
        foreach ( $product_attachment_ids as $attachment_id ) {
            $allowed = $this->coordinator->validate_editable_image_attachment( $attachment_id );
            if ( is_wp_error( $allowed ) ) {
                return $allowed;
            }
        }

        $recipe = Campaign_Set_Recipe::normalize( $command['recipe'] ?? null );
        if ( is_wp_error( $recipe ) ) {
            return $recipe;
        }
        $subject_attachment_id = (int) ( $recipe['subjectAttachmentId'] ?? 0 );
        $environment_attachment_id = (int) ( $recipe['environmentAttachmentId'] ?? 0 );
        foreach ( [ $subject_attachment_id, $environment_attachment_id ] as $attachment_id ) {
            if ( $attachment_id <= 0 ) {
                continue;
            }
            $allowed = $this->coordinator->validate_editable_image_attachment( $attachment_id );
            if ( is_wp_error( $allowed ) ) {
                return $allowed;
            }
        }

        $fingerprint_payload = [
            'target'               => (string) $context['run_target'],
            'subjectId'            => (int) $context['subject_id'],
            'productAttachmentIds' => $product_attachment_ids,
            'recipe'               => $recipe,
        ];
        if ( ! empty( $context['run_item_uuid'] ) ) {
            $fingerprint_payload['itemIdentity'] = [
                'runItemId'       => (string) $context['run_item_uuid'],
                'campaignGroupId' => (string) ( $context['campaign_group_uuid'] ?? '' ),
                'itemOrdinal'     => (int) ( $context['item_ordinal'] ?? 0 ),
                'target'          => Workflow_Item_Identity::target_hint( (array) $context['target_snapshot'] ),
            ];
        }
        $fingerprint = $this->coordinator->fingerprint_payload( self::WORKFLOW, $fingerprint_payload );
        if ( is_wp_error( $fingerprint ) ) {
            return $fingerprint;
        }
        $replay = $this->coordinator->replay_request( $user_id, self::WORKFLOW, $request_id, $fingerprint );
        if ( null !== $replay ) {
            return $replay;
        }
        $subject_guard = $this->coordinator->guard_subject_admission( $user_id, self::WORKFLOW, $request_id, $context );
        if ( is_wp_error( $subject_guard ) ) {
            return $subject_guard;
        }

		$count = (int) $recipe['count'];
		$trusted_pricing = is_array( $options['trusted_pricing'] ?? null ) ? $options['trusted_pricing'] : [];
		if ( [] !== $trusted_pricing ) {
			$verified_pricing = Workflow_Preflight::normalize_trusted_pricing( $trusted_pricing, self::WORKFLOW, $count );
			if ( is_wp_error( $verified_pricing ) ) {
				return $verified_pricing;
			}
			$pricing = [ 'credits_per_image' => $verified_pricing['creditsPerOutput'] ];
		} else {
			$refresh_pricing = ! array_key_exists( 'refresh_pricing', $options ) || ! empty( $options['refresh_pricing'] );
			$pricing = Price_Service::instance()->get_campaign_set_pricing( $refresh_pricing );
			if ( is_wp_error( $pricing ) ) {
				return $pricing;
			}
		}
		if ( [] === $trusted_pricing && ( $count < (int) $pricing['min_count'] || $count > (int) $pricing['max_count'] ) ) {
            return new \WP_Error(
                'fotogenic_ai_invalid_campaign_count',
                __( 'The selected Campaign Set count is not available for the current account.', 'fotogenic-ai' ),
                [ 'status' => 400 ]
            );
        }
        $estimated_cost = (float) $pricing['credits_per_image'] * $count;

        $resolver = Image_Source_Resolver::instance();
        $product_images = $resolver->get_campaign_image_files_for_attachments( $product_attachment_ids );
        if ( is_wp_error( $product_images ) ) {
            return $product_images;
        }
        $subject_image = null;
        if ( $subject_attachment_id > 0 ) {
            $subject_image = $resolver->get_image_file_for_workflow_attachment( $subject_attachment_id );
            if ( is_wp_error( $subject_image ) ) {
                return $subject_image;
            }
        }
        $environment_image = null;
        if ( $environment_attachment_id > 0 ) {
            $environment_image = $resolver->get_image_file_for_workflow_attachment( $environment_attachment_id );
            if ( is_wp_error( $environment_image ) ) {
                return $environment_image;
            }
        }
        $multipart = ( new Campaign_Set_Multipart_Builder() )->build(
            [
                'recipe'            => $recipe,
                'product_images'    => $product_images,
                'subject_image'     => $subject_image,
                'environment_image' => $environment_image,
            ]
        );
        if ( is_wp_error( $multipart ) ) {
            return $multipart;
        }

        $source_attachment_ids = array_values(
            array_unique(
                array_filter(
                    array_merge( $product_attachment_ids, [ $subject_attachment_id, $environment_attachment_id ] )
                )
            )
        );
        $metadata = [
            'source_attachment_id'  => (int) ( $product_attachment_ids[0] ?? 0 ),
            'source_attachment_ids' => $source_attachment_ids,
            'expected_output_count' => $count,
            'estimated_cost'        => $estimated_cost,
            'images_count'          => $count,
			'quality'               => (string) $recipe['quality'],
            'price_per_output'      => (float) $pricing['credits_per_image'],
        ];
        $metadata = array_merge( $metadata, $this->coordinator->item_identity_metadata( $context ) );
        $bulk_run_uuid = strtolower( trim( (string) ( $options['bulk_run_uuid'] ?? '' ) ) );
        $bulk_item_id  = (int) ( $options['bulk_item_id'] ?? 0 );
        if ( wp_is_uuid( $bulk_run_uuid ) && $bulk_item_id > 0 ) {
            $metadata['bulk_run_uuid'] = $bulk_run_uuid;
            $metadata['bulk_item_id']  = $bulk_item_id;
        }
        $admission = $this->coordinator->admit_request(
            $user_id,
            self::WORKFLOW,
            $request_id,
            $fingerprint,
            $context,
            $metadata,
            (string) ( $options['universal_fingerprint'] ?? '' )
        );
        if ( is_wp_error( $admission ) ) {
            return $admission;
        }
        if ( ! empty( $admission['replayed'] ) ) {
            return $admission['result'] ?? Messages::error( 'request_failed', 500 );
        }
		$idempotency_key = $this->coordinator->get_upstream_idempotency_key( $admission );
		if ( is_wp_error( $idempotency_key ) ) {
			return $idempotency_key;
		}

        $start = $this->coordinator->begin_start( $user_id, self::WORKFLOW, $request_id, $context, $metadata, $admission );
        if ( is_wp_error( $start ) ) {
            return $start;
        }
		$prepared_start = $this->coordinator->prepare_upstream_dispatch( $start );
		if ( is_wp_error( $prepared_start ) ) {
			return $this->coordinator->handle_upstream_error( $prepared_start, self::WORKFLOW, $request_id, $context, $start );
		}
		$start = $prepared_start;

        $api_response = Api_Client::instance()->request_multipart(
            'campaign_set_generator',
            $multipart['fields'],
            $multipart['files'],
            [
				'request_id'      => $request_id,
				'run_id'          => (string) $start['run_id'],
				'idempotency_key' => $idempotency_key,
            ]
        );
        if ( is_wp_error( $api_response ) ) {
            return $this->coordinator->handle_upstream_error( $api_response, self::WORKFLOW, $request_id, $context, $start );
        }

		$captured = $this->coordinator->capture_upstream_start_response( self::WORKFLOW, $start, $api_response );
		if ( is_wp_error( $captured ) ) {
			return $this->coordinator->handle_upstream_error( $captured, self::WORKFLOW, $request_id, $context, $start );
		}
		$start['request_record'] = $captured['request_record'];
		$job_ids = $captured['job_ids'];
		$result = $this->coordinator->complete_start( $user_id, self::WORKFLOW, $request_id, $context, $start, $job_ids, $metadata );
        if ( is_wp_error( $result ) ) {
            return $result;
        }
		$result['acceptedOutputCount'] = (int) $captured['accepted_output_count'];
        return $result;
    }

    /**
     * @param mixed $raw Raw attachment ID list.
     * @return array|\WP_Error
     */
    private function normalize_attachment_ids( $raw ) {
        if ( ! is_array( $raw ) ) {
            return new \WP_Error(
                'fotogenic_ai_invalid_campaign_images',
                __( 'Campaign Set requires 1 to 10 product image attachment IDs.', 'fotogenic-ai' ),
                [ 'status' => 400 ]
            );
        }
        $ids = [];
        foreach ( $raw as $value ) {
            if ( ! is_int( $value ) && ! ( is_string( $value ) && ctype_digit( $value ) ) ) {
                return new \WP_Error(
                    'fotogenic_ai_invalid_campaign_images',
                    __( 'Campaign Set image values must be attachment IDs.', 'fotogenic-ai' ),
                    [ 'status' => 400 ]
                );
            }
            $attachment_id = absint( $value );
            if ( $attachment_id <= 0 ) {
                return new \WP_Error(
                    'fotogenic_ai_invalid_campaign_images',
                    __( 'Campaign Set image values must be attachment IDs.', 'fotogenic-ai' ),
                    [ 'status' => 400 ]
                );
            }
            $ids[] = $attachment_id;
        }
        $ids = array_values( array_unique( $ids ) );
        if ( count( $ids ) < self::MIN_INPUTS || count( $ids ) > self::MAX_INPUTS ) {
            return new \WP_Error(
                'fotogenic_ai_invalid_campaign_images',
                __( 'Campaign Set requires 1 to 10 product image attachment IDs.', 'fotogenic-ai' ),
                [ 'status' => 400 ]
            );
        }
        return $ids;
    }
}
