<?php
/**
 * Ability: wp-agent/create-post-from-markdown
 *
 * The only content write path. Converts Markdown to valid Gutenberg blocks
 * server-side and creates or updates a post, returning a block-validation
 * report so the pipeline can gate on freeform fallback.
 *
 * @package WPAgentCompanion
 */

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

function wpagent_ability_create_post_from_markdown(): array {
	return array(
		'label'               => __( 'Post aus Markdown erstellen', 'wp-agent-companion' ),
		'description'         => __( 'Konvertiert Markdown serverseitig in valide Gutenberg-Blocks und legt den Beitrag an oder aktualisiert ihn. Einziger Content-Schreibpfad. Verhindert den stillen core/freeform-Fallback.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'       => 'object',
			'properties' => array(
				'markdown' => array(
					'type'        => 'string',
					'description' => __( 'Artikeltext in Markdown.', 'wp-agent-companion' ),
				),
				'title'    => array(
					'type'        => 'string',
					'description' => __( 'Beitragstitel.', 'wp-agent-companion' ),
				),
				'status'   => array(
					'type'    => 'string',
					'enum'    => array( 'draft', 'publish', 'pending', 'private' ),
					'default' => 'draft',
				),
				'slug'     => array( 'type' => 'string' ),
				'excerpt'  => array( 'type' => 'string' ),
				'category' => array(
					'type'        => 'string',
					'description' => __( 'Kategoriename; wird bei Bedarf angelegt und exklusiv gesetzt.', 'wp-agent-companion' ),
				),
				'post_id'  => array(
					'type'        => 'integer',
					'description' => __( 'Wenn gesetzt, wird dieser Beitrag aktualisiert (Idempotenz).', 'wp-agent-companion' ),
				),
				'post_type' => array(
					'type'        => 'string',
					'description' => __( 'Zieltyp für neue Inhalte; muss ein registrierter, öffentlicher Post-Type sein (z. B. post, page). Bei Updates ohne post_type bleibt der bestehende Typ erhalten.', 'wp-agent-companion' ),
				),
			),
			'required'   => array( 'markdown', 'title' ),
		),
		'output_schema'       => array(
			'type'       => 'object',
			'properties' => array(
				'post_id'        => array( 'type' => 'integer' ),
				'url'            => array( 'type' => 'string' ),
				'status'         => array( 'type' => 'string' ),
				'content_hash'   => array( 'type' => 'string' ),
				'block_count'    => array( 'type' => 'integer' ),
				'freeform_count' => array( 'type' => 'integer' ),
				'block_types'    => array(
					'type'  => 'array',
					'items' => array( 'type' => 'string' ),
				),
				'updated'        => array( 'type' => 'boolean' ),
			),
		),
		'execute_callback'    => 'wpagent_exec_create_post_from_markdown',
		'permission_callback' => 'wpagent_perm_edit_posts',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				'idempotent'  => true,
			),
		),
	);
}

/**
 * Capture the fields a content write can change, including dates WordPress may
 * alter as a side effect of a status transition.
 *
 * @param WP_Post $post
 * @return array<string,int|string>
 */
function wpagent_content_write_snapshot( WP_Post $post ): array {
	return array(
		'post_title'    => $post->post_title,
		'post_content'  => $post->post_content,
		'post_excerpt'  => $post->post_excerpt,
		'post_status'   => $post->post_status,
		'post_name'     => $post->post_name,
		'post_type'     => $post->post_type,
		'post_date'     => $post->post_date,
		'post_date_gmt' => $post->post_date_gmt,
	);
}

/**
 * @param WP_Post                  $post
 * @param array<string,int|string> $snapshot
 */
function wpagent_content_write_snapshot_matches( WP_Post $post, array $snapshot ): bool {
	foreach ( $snapshot as $field => $expected ) {
		if ( ! property_exists( $post, $field ) || (string) $post->{$field} !== (string) $expected ) {
			return false;
		}
	}
	return true;
}

/**
 * Restore an updated post or permanently remove a post created by the failed
 * operation. The latter deliberately also covers already-published partials.
 *
 * @param int                           $post_id
 * @param array<string,int|string>|null $snapshot Null means the post was new.
 */
function wpagent_compensate_content_write( int $post_id, ?array $snapshot ): bool {
	if ( null === $snapshot ) {
		if ( null === get_post( $post_id ) ) {
			return true;
		}
		$deleted = wp_delete_post( $post_id, true );
		return false !== $deleted && null === get_post( $post_id );
	}
	$current = get_post( $post_id );
	if ( $current instanceof WP_Post && wpagent_content_write_snapshot_matches( $current, $snapshot ) ) {
		return true;
	}

	$restore = array_merge( array( 'ID' => $post_id, 'edit_date' => true ), $snapshot );
	$result  = wp_update_post( wp_slash( $restore ), true );
	$fresh   = get_post( $post_id );
	return ! is_wp_error( $result )
		&& $fresh instanceof WP_Post
		&& wpagent_content_write_snapshot_matches( $fresh, $snapshot );
}

/**
 * @param WP_Error                      $failure
 * @param int                           $post_id
 * @param array<string,int|string>|null $snapshot
 * @return WP_Error
 */
function wpagent_content_write_failure_after_compensation( WP_Error $failure, int $post_id, ?array $snapshot ): WP_Error {
	if ( wpagent_compensate_content_write( $post_id, $snapshot ) ) {
		$data = $failure->get_error_data();
		$data = is_array( $data ) ? $data : array();
		$data['rollback_verified'] = true;
		$failure->add_data( $data );
		return $failure;
	}

	return new WP_Error(
		'wpagent_post_rollback_failed',
		__( 'Der unvollständige Inhaltsschreibvorgang konnte nicht sicher zurückgerollt werden. Prüfe den Beitrag sofort manuell.', 'wp-agent-companion' ),
		array( 'status' => 500, 'rollback_verified' => false, 'original_error' => $failure->get_error_code() )
	);
}

/**
 * @param WP_Post              $post
 * @param array<string,mixed>  $expected
 */
function wpagent_content_write_matches( WP_Post $post, array $expected ): bool {
	foreach ( $expected as $field => $value ) {
		if ( ! property_exists( $post, $field ) || (string) $post->{$field} !== (string) $value ) {
			return false;
		}
	}
	return true;
}

/**
 * Return only field names and hashes, never persisted content itself.
 *
 * @param WP_Post             $post
 * @param array<string,mixed> $expected
 * @return string[]
 */
function wpagent_content_write_mismatches( WP_Post $post, array $expected ): array {
	$mismatches = array();
	foreach ( $expected as $field => $value ) {
		if ( ! property_exists( $post, $field ) || (string) $post->{$field} !== (string) $value ) {
			$mismatches[] = $field;
		}
	}
	return $mismatches;
}

/**
 * @param WP_Error                      $failure
 * @param int                           $post_id
 * @param array<string,int|string>|null $snapshot
 * @param int[]|null                    $category_snapshot Null when no category write was requested.
 */
function wpagent_markdown_failure_after_compensation( WP_Error $failure, int $post_id, ?array $snapshot, ?array $category_snapshot, int $created_category_id ): WP_Error {
	$post_restored = wpagent_compensate_content_write( $post_id, $snapshot );
	$terms_restored = true;
	if ( null !== $snapshot && null !== $category_snapshot ) {
		$restored_terms = wp_set_post_terms( $post_id, $category_snapshot, 'category', false );
		$terms_restored = ! is_wp_error( $restored_terms );
	}
	$term_removed = true;
	if ( $created_category_id > 0 && term_exists( $created_category_id, 'category' ) ) {
		$objects = get_objects_in_term( $created_category_id, 'category' );
		if ( is_wp_error( $objects ) ) {
			$term_removed = false;
		} else {
			$foreign_objects = array_diff( array_map( 'intval', $objects ), array( $post_id ) );
			if ( array() !== $foreign_objects ) {
				$term_removed = false;
			} else {
				$deleted      = wp_delete_term( $created_category_id, 'category' );
				$term_removed = ! is_wp_error( $deleted ) && ! term_exists( $created_category_id, 'category' );
			}
		}
	}
	if ( $terms_restored && null !== $snapshot && null !== $category_snapshot ) {
		$fresh_terms = wp_get_post_terms( $post_id, 'category', array( 'fields' => 'ids' ) );
		if ( is_wp_error( $fresh_terms ) ) {
			$terms_restored = false;
		} else {
			$actual = array_map( 'intval', $fresh_terms );
			sort( $actual, SORT_NUMERIC );
			sort( $category_snapshot, SORT_NUMERIC );
			$terms_restored = $actual === $category_snapshot;
		}
	}
	if ( $post_restored && $terms_restored && $term_removed ) {
		$data = $failure->get_error_data();
		$data = is_array( $data ) ? $data : array();
		$data['rollback_verified'] = true;
		$failure->add_data( $data );
		return $failure;
	}
	return new WP_Error(
		'wpagent_post_rollback_failed',
		__( 'Der fehlgeschlagene Beitrags- oder Kategorie-Schreibvorgang konnte nicht vollständig zurückgerollt werden. Prüfe Beitrag und Kategorien sofort manuell.', 'wp-agent-companion' ),
		array(
			'status'               => 500,
			'rollback_verified'    => false,
			'original_error'       => $failure->get_error_code(),
			'post_restored'        => $post_restored,
			'terms_restored'       => $terms_restored,
			'created_term_removed' => $term_removed,
		)
	);
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_create_post_from_markdown( $input ) {
	$input    = is_array( $input ) ? $input : array();
	$markdown = (string) ( $input['markdown'] ?? '' );
	$title    = trim( (string) ( $input['title'] ?? '' ) );

	if ( '' === $title ) {
		return new WP_Error( 'wpagent_missing_title', __( 'Titel fehlt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	$updated       = false;
	$post_id       = (int) ( $input['post_id'] ?? 0 );
	$existing_post = null;
	if ( $post_id > 0 ) {
		$existing_post = get_post( $post_id );
		if ( ! $existing_post ) {
			return new WP_Error( 'wpagent_no_post', __( 'Beitrag nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung für diesen Beitrag.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		$detection = wpagent_exec_detect_builder( array( 'post_id' => $post_id ) );
		if ( is_wp_error( $detection ) ) {
			return $detection;
		}
		if ( ! wpagent_builder_content_write_supported( $detection, $existing_post->post_type ) ) {
			return wpagent_builder_content_write_error( $detection );
		}
	}

	$post_type_provided = array_key_exists( 'post_type', $input ) && '' !== trim( (string) $input['post_type'] );
	if ( $post_type_provided ) {
		$post_type = (string) $input['post_type'];
	} elseif ( $existing_post instanceof WP_Post ) {
		$post_type = $existing_post->post_type;
	} else {
		$post_type = 'post';
	}
	if ( $existing_post instanceof WP_Post && $post_type_provided && $post_type !== $existing_post->post_type ) {
		return new WP_Error( 'wpagent_post_type_change_forbidden', __( 'Der Post-Type eines bestehenden Beitrags darf über diese Ability nicht geändert werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$type_valid = wpagent_validate_post_type( $post_type );
	if ( is_wp_error( $type_valid ) ) {
		return $type_valid;
	}
	if ( ! $existing_post && ! current_user_can( wpagent_post_type_create_cap( $post_type ) ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung, Inhalte für diesen Post-Type zu erstellen.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	if ( ! current_user_can( wpagent_post_type_edit_cap( $post_type ) ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung für diesen Post-Type.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}

	$status  = (string) ( $input['status'] ?? 'draft' );
	if ( ! in_array( $status, array( 'draft', 'publish', 'pending', 'private' ), true ) ) {
		$status = 'draft';
	}
	$status_cap = wpagent_check_post_status_cap( $post_type, $status );
	if ( is_wp_error( $status_cap ) ) {
		return $status_cap;
	}

	$category_requested  = ! empty( $input['category'] );
	$category_snapshot   = null;
	$created_category_id = 0;
	$term_id             = 0;
	$category_to_create  = '';
	if ( $category_requested ) {
		if ( ! is_object_in_taxonomy( $post_type, 'category' ) ) {
			return new WP_Error( 'wpagent_category_not_supported', __( 'Der gewählte Post-Type unterstützt keine Kategorien.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$category   = get_taxonomy( 'category' );
		$assign_cap = ( $category && ! empty( $category->cap->assign_terms ) ) ? (string) $category->cap->assign_terms : 'do_not_allow';
		if ( ! current_user_can( $assign_cap ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung, Kategorien zuzuordnen.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}

		$cat_name = trim( (string) $input['category'] );
		$term     = get_term_by( 'name', $cat_name, 'category' );
		if ( ! $term ) {
			$manage_cap = ( $category && ! empty( $category->cap->manage_terms ) ) ? (string) $category->cap->manage_terms : 'do_not_allow';
			if ( ! current_user_can( $manage_cap ) ) {
				return new WP_Error( 'wpagent_forbidden_manage_terms', __( 'Die Kategorie existiert noch nicht. Zum automatischen Anlegen ist die manage_terms-Berechtigung dieser Taxonomie erforderlich.', 'wp-agent-companion' ), array( 'status' => 403 ) );
			}
			$category_to_create = $cat_name;
		} else {
			$term_id = (int) $term->term_id;
		}
		if ( $existing_post instanceof WP_Post ) {
			$before_terms = wp_get_post_terms( $post_id, 'category', array( 'fields' => 'ids' ) );
			if ( is_wp_error( $before_terms ) ) {
				if ( $created_category_id > 0 ) {
					wp_delete_term( $created_category_id, 'category' );
				}
				return $before_terms;
			}
			$category_snapshot = array_map( 'intval', $before_terms );
		}
	}

	$converter = new WPAgent_Markdown_To_Blocks();
	$content   = $converter->convert( $markdown );
	$report    = wpagent_block_report( $content );
	$validated = wpagent_validate_generated_core_blocks( $content );
	if ( is_wp_error( $validated ) ) {
		return $validated;
	}
	if ( '' !== $category_to_create ) {
		$new = wp_insert_term( $category_to_create, 'category' );
		if ( is_wp_error( $new ) ) {
			$existing_id = ( 'term_exists' === $new->get_error_code() ) ? (int) $new->get_error_data() : 0;
			if ( $existing_id <= 0 ) {
				return $new;
			}
			$term_id = $existing_id;
		} else {
			$term_id             = (int) $new['term_id'];
			$created_category_id = $term_id;
		}
	}
	if ( $category_requested && $term_id <= 0 ) {
		return new WP_Error( 'wpagent_category_failed', __( 'Kategorie konnte nicht aufgelöst werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}

	$postarr = array(
		'post_title'   => $title,
		'post_content' => $content,
		'post_status'  => $status,
		'post_type'    => $post_type,
	);
	if ( ! empty( $input['slug'] ) ) {
		$postarr['post_name'] = sanitize_title( (string) $input['slug'] );
	}
	if ( ! empty( $input['excerpt'] ) ) {
		$postarr['post_excerpt'] = (string) $input['excerpt'];
	}

	$post_snapshot = $existing_post instanceof WP_Post ? wpagent_content_write_snapshot( $existing_post ) : null;
	if ( $post_id > 0 ) {
		$postarr['ID'] = $post_id;
		$res           = wp_update_post( wp_slash( $postarr ), true );
		$updated       = true;
	} else {
		$res = wp_insert_post( wp_slash( $postarr ), true );
	}

	if ( is_wp_error( $res ) ) {
		if ( null !== $post_snapshot ) {
			return wpagent_markdown_failure_after_compensation( $res, $post_id, $post_snapshot, $category_snapshot, $created_category_id );
		}
		if ( $created_category_id > 0 ) {
			$objects = get_objects_in_term( $created_category_id, 'category' );
			$deleted = ! is_wp_error( $objects ) && array() === $objects
				? wp_delete_term( $created_category_id, 'category' )
				: new WP_Error( 'wpagent_category_in_use', __( 'Die neue Kategorie wird inzwischen von einem anderen Inhalt verwendet.', 'wp-agent-companion' ) );
			if ( is_wp_error( $deleted ) || term_exists( $created_category_id, 'category' ) ) {
				return new WP_Error(
					'wpagent_post_rollback_failed',
					__( 'Die vor dem fehlgeschlagenen Beitragsschreibvorgang neu angelegte Kategorie konnte nicht entfernt werden. Prüfe die Kategorien sofort manuell.', 'wp-agent-companion' ),
					array( 'status' => 500, 'rollback_verified' => false, 'original_error' => $res->get_error_code() )
				);
			}
		}
		return $res;
	}
	$post_id = (int) $res;

	$fresh    = get_post( $post_id );
	$expected = array(
		'post_title'   => $title,
		'post_content' => $content,
		'post_status'  => $status,
		'post_type'    => $post_type,
	);
	if ( isset( $postarr['post_name'] ) ) {
		$expected['post_name'] = $postarr['post_name'];
	}
	if ( isset( $postarr['post_excerpt'] ) ) {
		$expected['post_excerpt'] = $postarr['post_excerpt'];
	}
	if ( ! $fresh instanceof WP_Post || ! wpagent_content_write_matches( $fresh, $expected ) ) {
		$mismatches = $fresh instanceof WP_Post ? wpagent_content_write_mismatches( $fresh, $expected ) : array( 'post_missing' );
		$error_data = array( 'status' => 502, 'mismatched_fields' => $mismatches );
		if ( $fresh instanceof WP_Post && in_array( 'post_content', $mismatches, true ) ) {
			$error_data['expected_content_hash']  = hash( 'sha256', $content );
			$error_data['actual_content_hash']    = hash( 'sha256', $fresh->post_content );
			$error_data['expected_content_bytes'] = strlen( $content );
			$error_data['actual_content_bytes']   = strlen( $fresh->post_content );
		}
		return wpagent_markdown_failure_after_compensation(
			new WP_Error( 'wpagent_post_write_unverified', __( 'WordPress hat den angeforderten Beitragsinhalt nicht exakt gespeichert.', 'wp-agent-companion' ), $error_data ),
			$post_id,
			$post_snapshot,
			$category_snapshot,
			$created_category_id
		);
	}

	// Assign exactly one category by name (create if missing).
	if ( $category_requested ) {
		$assigned = wp_set_post_terms( $post_id, array( $term_id ), 'category', false );
		if ( is_wp_error( $assigned ) ) {
			return wpagent_markdown_failure_after_compensation( $assigned, $post_id, $post_snapshot, $category_snapshot, $created_category_id );
		}
		$read_back = wp_get_post_terms( $post_id, 'category', array( 'fields' => 'ids' ) );
		if ( is_wp_error( $read_back ) ) {
			return wpagent_markdown_failure_after_compensation( $read_back, $post_id, $post_snapshot, $category_snapshot, $created_category_id );
		}
		$assigned_ids = array_map( 'intval', $read_back );
		if ( array( $term_id ) !== $assigned_ids ) {
			return wpagent_markdown_failure_after_compensation(
				new WP_Error( 'wpagent_category_unverified', __( 'Die angeforderte Kategorie konnte nicht bestätigt werden.', 'wp-agent-companion' ), array( 'status' => 502 ) ),
				$post_id,
				$post_snapshot,
				$category_snapshot,
				$created_category_id
			);
		}
	}

	return array(
		'post_id'        => $post_id,
		'url'            => get_permalink( $post_id ),
		'status'         => get_post_status( $post_id ),
		'content_hash'   => hash( 'sha256', $content ),
		'block_count'    => $report['block_count'],
		'freeform_count' => $report['freeform_count'],
		'block_types'    => $report['block_types'],
		'updated'        => $updated,
	);
}
