<?php
/** Verified WooCommerce order status and note writes. */

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

/** @return array<string,mixed> */
function wpagent_wc_order_write_output_schema(): array {
	return array(
		'type'                 => 'object',
		'additionalProperties' => false,
		'properties'           => array(
			'order'    => wpagent_wc_order_output_schema(),
			'verified' => array( 'type' => 'boolean' ),
		),
		'required'             => array( 'order', 'verified' ),
	);
}

function wpagent_ability_set_order_status(): array {
	return array(
		'label'               => __( 'WooCommerce-Bestellstatus setzen', 'wp-agent-companion' ),
		'description'         => __( 'Setzt einen registrierten Bestellstatus über WooCommerce CRUD mit Konfliktprüfung und Read-back. Die Änderung kann E-Mails, Lagerbewegungen, Webhooks und Drittanbieter-Automationen auslösen und erfordert confirm=true. Ein Erstattungsstatus wird ohne eigenen Erstattungsvorgang abgelehnt.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'order_id'               => array( 'type' => 'integer', 'minimum' => 1 ),
				'status'                 => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 ),
				'expected_date_modified' => array( 'type' => 'string', 'maxLength' => 40 ),
				'confirm'                => array( 'type' => 'boolean' ),
			),
			'required'             => array( 'order_id', 'status', 'confirm' ),
		),
		'output_schema'       => wpagent_wc_order_write_output_schema(),
		'execute_callback'    => 'wpagent_exec_set_order_status',
		'permission_callback' => 'wpagent_perm_wc_orders_edit',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => true ) ),
	);
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_set_order_status( $input ) {
	$input = is_array( $input ) ? $input : array();
	if ( ! wpagent_wc_input_is_true( $input['confirm'] ?? false ) ) {
		return new WP_Error( 'wpagent_confirmation_required', __( 'Bestellstatusänderungen erfordern confirm=true, weil sie Kunden-E-Mails, Lagerbewegungen, Webhooks oder Drittanbieter-Automationen auslösen können.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$status = wpagent_wc_normalize_order_status( $input['status'] ?? '', true );
	if ( is_wp_error( $status ) ) {
		return $status;
	}
	$order = wpagent_wc_get_authorized_order( $input, 'edit_shop_orders' );
	if ( is_wp_error( $order ) ) {
		return $order;
	}
	if ( ! wpagent_wc_order_expected_version_matches( $input, $order ) ) {
		return new WP_Error( 'wpagent_wc_order_conflict', __( 'Die WooCommerce-Bestellung wurde seit dem gelesenen Stand verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$lock = wpagent_companion_acquire_connection_lock();
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$fresh = wc_get_order( $order->get_id() );
		if ( ! $fresh instanceof WC_Order || $fresh instanceof WC_Order_Refund ) {
			return new WP_Error( 'wpagent_wc_order_not_found', __( 'WooCommerce-Bestellung nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		if ( ! wpagent_wc_order_expected_version_matches( $input, $fresh ) ) {
			return new WP_Error( 'wpagent_wc_order_conflict', __( 'Die WooCommerce-Bestellung wurde seit dem gelesenen Stand verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$old_status = $fresh->get_status();
		if ( $old_status === $status ) {
			return array( 'order' => wpagent_wc_order_payload( $fresh ), 'verified' => true );
		}
		try {
			$fresh->set_status( $status );
			$saved_id = $fresh->save();
		} catch ( Throwable $error ) {
			return new WP_Error( 'wpagent_wc_order_status_failed', __( 'Der WooCommerce-Bestellstatus konnte nicht gespeichert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		$readback = $saved_id ? wc_get_order( $fresh->get_id() ) : false;
		if ( ! $readback instanceof WC_Order || $readback->get_status() !== $status ) {
			$rollback_succeeded = false;
			try {
				$rollback_order = wc_get_order( $fresh->get_id() );
				if ( $rollback_order instanceof WC_Order ) {
					$rollback_order->set_status( $old_status );
					$rollback_succeeded = (bool) $rollback_order->save();
				}
			} catch ( Throwable $error ) {
				$rollback_succeeded = false;
			}
			return new WP_Error( 'wpagent_wc_order_verify_failed', __( 'Die Bestellstatusänderung konnte nicht verifiziert werden und wurde zurückgesetzt, soweit WooCommerce dies zugelassen hat.', 'wp-agent-companion' ), array( 'status' => 500, 'rollback_succeeded' => $rollback_succeeded ) );
		}
		return array( 'order' => wpagent_wc_order_payload( $readback ), 'verified' => true );
	} finally {
		wpagent_companion_release_connection_lock( $lock );
	}
}

/** @return array<string,mixed> */
function wpagent_wc_order_note_write_output_schema(): array {
	return array(
		'type'                 => 'object',
		'additionalProperties' => false,
		'properties'           => array(
			'note'     => wpagent_wc_order_note_output_schema(),
			'verified' => array( 'type' => 'boolean' ),
			'reused'   => array( 'type' => 'boolean' ),
		),
		'required'             => array( 'note', 'verified', 'reused' ),
	);
}

function wpagent_ability_add_order_note(): array {
	return array(
		'label'               => __( 'WooCommerce-Bestellnotiz hinzufügen', 'wp-agent-companion' ),
		'description'         => __( 'Fügt eine Nur-Text-Bestellnotiz über WooCommerce hinzu und verifiziert sie. Kundensichtbare Notizen können eine E-Mail auslösen und benötigen eine ausdrückliche Bestätigung. Ein Idempotenzschlüssel verhindert doppelte Notizen bei Wiederholungen.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'order_id'                      => array( 'type' => 'integer', 'minimum' => 1 ),
				'note'                          => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 2000 ),
				'customer_note'                 => array( 'type' => 'boolean', 'default' => false ),
				'confirm_customer_notification' => array( 'type' => 'boolean', 'default' => false ),
				'idempotency_key'               => array( 'type' => 'string', 'minLength' => 8, 'maxLength' => 64, 'pattern' => '^[A-Za-z0-9._:-]+$' ),
			),
			'required'             => array( 'order_id', 'note', 'idempotency_key' ),
		),
		'output_schema'       => wpagent_wc_order_note_write_output_schema(),
		'execute_callback'    => 'wpagent_exec_add_order_note',
		'permission_callback' => 'wpagent_perm_wc_orders_edit',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => true ) ),
	);
}

/** @return WP_Comment[] */
function wpagent_wc_find_order_notes_by_idempotency_key( int $order_id, string $idempotency_key ): array {
	$woocommerce_filter = array( 'WC_Comments', 'exclude_order_comments' );
	$filter_was_active  = has_filter( 'comments_clauses', $woocommerce_filter );
	if ( false !== $filter_was_active ) {
		remove_filter( 'comments_clauses', $woocommerce_filter, (int) $filter_was_active );
	}
	try {
		$comments = get_comments(
			array(
				'post_id'    => $order_id,
				'type'       => 'order_note',
				'status'     => 'approve',
				'number'     => 2,
				'meta_key'   => '_wpagent_idempotency_key',
				'meta_value' => $idempotency_key,
			)
		);
		return array_values( array_filter( $comments, static fn( $comment ): bool => $comment instanceof WP_Comment ) );
	} finally {
		if ( false !== $filter_was_active ) {
			add_filter( 'comments_clauses', $woocommerce_filter, (int) $filter_was_active );
		}
	}
}

function wpagent_wc_order_note_character_length( string $value ): int {
	$bytes      = strlen( $value );
	$characters = 0;
	for ( $index = 0; $index < $bytes; $index++ ) {
		if ( ( ord( $value[ $index ] ) & 0xC0 ) !== 0x80 ) {
			$characters++;
		}
	}
	return $characters;
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_add_order_note( $input ) {
	$input = is_array( $input ) ? $input : array();
	$order = wpagent_wc_get_authorized_order( $input, 'edit_shop_orders' );
	if ( is_wp_error( $order ) ) {
		return $order;
	}
	$note = sanitize_textarea_field( (string) ( $input['note'] ?? '' ) );
	if ( '' === trim( $note ) || wpagent_wc_order_note_character_length( $note ) > 2000 ) {
		return new WP_Error( 'wpagent_wc_bad_order_note', __( 'note muss nach der Nur-Text-Bereinigung zwischen 1 und 2.000 Zeichen enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$customer_note = wpagent_wc_input_is_true( $input['customer_note'] ?? false );
	if ( $customer_note && ! wpagent_wc_input_is_true( $input['confirm_customer_notification'] ?? false ) ) {
		return new WP_Error( 'wpagent_confirmation_required', __( 'Eine kundensichtbare Bestellnotiz kann eine E-Mail auslösen und erfordert confirm_customer_notification=true.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$idempotency_key = (string) ( $input['idempotency_key'] ?? '' );
	if ( ! preg_match( '/^[A-Za-z0-9._:-]{8,64}$/', $idempotency_key ) ) {
		return new WP_Error( 'wpagent_wc_bad_idempotency_key', __( 'idempotency_key muss 8 bis 64 Zeichen aus Buchstaben, Zahlen, Punkt, Unterstrich, Doppelpunkt oder Bindestrich enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$payload_hash = hash( 'sha256', wp_json_encode( array( 'note' => $note, 'customer_note' => $customer_note ) ) );
	$lock         = wpagent_companion_acquire_connection_lock();
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$fresh = wc_get_order( $order->get_id() );
		if ( ! $fresh instanceof WC_Order || $fresh instanceof WC_Order_Refund ) {
			return new WP_Error( 'wpagent_wc_order_not_found', __( 'WooCommerce-Bestellung nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		$existing = wpagent_wc_find_order_notes_by_idempotency_key( $fresh->get_id(), $idempotency_key );
		if ( count( $existing ) > 1 ) {
			return new WP_Error( 'wpagent_wc_duplicate_idempotency_key', __( 'Der Idempotenzschlüssel ist dieser Bestellung mehrfach zugeordnet und muss manuell bereinigt werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		if ( 1 === count( $existing ) ) {
			$comment = $existing[0];
			$stored  = (string) get_comment_meta( $comment->comment_ID, '_wpagent_payload_hash', true );
			if ( ! hash_equals( $stored, $payload_hash ) ) {
				return new WP_Error( 'wpagent_wc_idempotency_conflict', __( 'Der Idempotenzschlüssel wurde bereits mit einer anderen Bestellnotiz verwendet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
			$existing_note = wc_get_order_note( $comment );
			if ( ! is_object( $existing_note ) ) {
				return new WP_Error( 'wpagent_wc_order_note_not_found', __( 'Die bereits gespeicherte Bestellnotiz konnte nicht gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
			}
			return array( 'note' => wpagent_wc_order_note_payload( $existing_note ), 'verified' => true, 'reused' => true );
		}

		$note_id = $fresh->add_order_note(
			$note,
			$customer_note ? 1 : 0,
			true,
			array(
				'_wpagent_idempotency_key' => $idempotency_key,
				'_wpagent_payload_hash'    => $payload_hash,
			)
		);
		$readback = $note_id ? wc_get_order_note( $note_id ) : null;
		$verified = is_object( $readback )
			&& $fresh->get_id() === absint( $readback->order_id ?? 0 )
			&& $note === sanitize_textarea_field( wp_strip_all_tags( (string) ( $readback->content ?? '' ) ) )
			&& $customer_note === ! empty( $readback->customer_note )
			&& hash_equals( $idempotency_key, (string) get_comment_meta( $note_id, '_wpagent_idempotency_key', true ) )
			&& hash_equals( $payload_hash, (string) get_comment_meta( $note_id, '_wpagent_payload_hash', true ) );
		if ( ! $verified ) {
			$rollback_succeeded = $note_id ? (bool) wp_delete_comment( $note_id, true ) : true;
			return new WP_Error( 'wpagent_wc_order_note_verify_failed', __( 'Die Bestellnotiz konnte nicht verifiziert werden und wurde entfernt, soweit WordPress dies zugelassen hat.', 'wp-agent-companion' ), array( 'status' => 500, 'rollback_succeeded' => $rollback_succeeded ) );
		}
		return array( 'note' => wpagent_wc_order_note_payload( $readback ), 'verified' => true, 'reused' => false );
	} finally {
		wpagent_companion_release_connection_lock( $lock );
	}
}
