<?php
/**
 * Structured lifecycle boundary for interactive founder tasks.
 *
 * @package Niwa_AI
 */

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

class Niwa_AI_Admin_Autonomy_Task_Checkpoint {

	public static function build_payload( $args ) {
		$args  = is_array( $args ) ? $args : array();
		$state = isset( $args['state'] ) ? sanitize_key( (string) $args['state'] ) : '';
		if ( ! in_array( $state, array( 'completed', 'continue', 'blocked' ), true ) ) {
			return self::error( 'task_checkpoint state must be completed, continue, or blocked.' );
		}

		$final_reply   = isset( $args['final_reply'] ) && is_string( $args['final_reply'] )
			? self::truncate_reply( $args['final_reply'], 12000 )
			: '';
		$remaining_work = self::normalize_text_list(
			isset( $args['remaining_work'] ) ? $args['remaining_work'] : array(),
			20,
			500
		);
		$blocker = self::normalize_blocker( $args['blocker'] ?? array() );

		if ( 'continue' === $state && empty( $remaining_work ) ) {
			return self::error( 'task_checkpoint continue requires concrete remaining_work.' );
		}
		if ( in_array( $state, array( 'completed', 'blocked' ), true ) && '' === $final_reply ) {
			return self::error( 'task_checkpoint completed or blocked requires final_reply.' );
		}
		if ( 'blocked' === $state && empty( $blocker ) ) {
			return self::error( 'task_checkpoint blocked requires a structured external blocker with kind, description, and evidence_call_id. Internal tool, iteration, or time budgets must use state=continue.' );
		}

		$evidence_call_ids = array();
		foreach ( isset( $args['evidence_call_ids'] ) && is_array( $args['evidence_call_ids'] ) ? $args['evidence_call_ids'] : array() as $call_id ) {
			if ( ! is_scalar( $call_id ) ) {
				continue;
			}
			$call_id = sanitize_text_field( (string) $call_id );
			if ( '' !== $call_id ) {
				$evidence_call_ids[] = $call_id;
			}
			if ( count( $evidence_call_ids ) >= 40 ) {
				break;
			}
		}

		return array(
			'ok'                    => true,
			'success'               => true,
			'task_checkpoint_state' => $state,
			'final_reply'           => 'continue' === $state ? '' : $final_reply,
			'remaining_work'        => 'continue' === $state ? $remaining_work : array(),
			'blocker'               => 'blocked' === $state ? $blocker : array(),
			'evidence_call_ids'     => array_values( array_unique( $evidence_call_ids ) ),
			'terminal_after_tool'   => true,
			'terminal_reason'       => 'task_checkpoint_' . $state,
		);
	}

	public static function apply_to_result( $result, $checkpoint, $open_requirements = array() ) {
		$result     = is_array( $result ) ? $result : array();
		$checkpoint = is_array( $checkpoint ) ? $checkpoint : array();
		if ( empty( $checkpoint['success'] ) ) {
			return $result;
		}

		$state = sanitize_key( (string) ( $checkpoint['task_checkpoint_state'] ?? '' ) );
		if ( ! in_array( $state, array( 'completed', 'continue', 'blocked' ), true ) ) {
			return $result;
		}

		$remaining_work = self::normalize_text_list(
			isset( $checkpoint['remaining_work'] ) ? $checkpoint['remaining_work'] : array(),
			20,
			500
		);
		$result['task_checkpoint_state'] = $state;
		$result['task_checkpoint']       = array(
			'state'             => $state,
			'remaining_work'    => $remaining_work,
			'blocker'           => isset( $checkpoint['blocker'] ) && is_array( $checkpoint['blocker'] )
				? $checkpoint['blocker']
				: array(),
			'evidence_call_ids' => isset( $checkpoint['evidence_call_ids'] ) && is_array( $checkpoint['evidence_call_ids'] )
				? array_values( $checkpoint['evidence_call_ids'] )
				: array(),
		);

		if ( 'completed' === $state || 'blocked' === $state ) {
			$result['success']                 = true;
			$result['reply']                   = self::truncate_reply( (string) ( $checkpoint['final_reply'] ?? ( $result['reply'] ?? '' ) ), 12000 );
			$result['task_completion_state']   = $state;
			$result['original_task_completed'] = 'completed' === $state;
			if ( 'blocked' === $state ) {
				$result['retry_original_task'] = false;
			}
			return $result;
		}

		$snapshot = isset( $result['context_snapshot'] ) && is_array( $result['context_snapshot'] )
			? $result['context_snapshot']
			: array();
		if ( empty( $remaining_work ) || empty( $snapshot['messages'] ) || ! is_array( $snapshot['messages'] ) ) {
			$result['success']                 = false;
			$result['reply']                   = '';
			$result['error']                   = 'Task checkpoint continuation could not be saved safely.';
			$result['task_completion_state']   = 'continuation_checkpoint_failed';
			$result['original_task_completed'] = false;
			return $result;
		}

		$control_lines = array(
			'[TASK CHECKPOINT CONTINUATION]',
			'The original founder task is unfinished. Continue from the exact saved tool trace without asking the founder to repeat or reconfirm it.',
			'Complete and verify every remaining item, then finish with task_checkpoint again.',
			'Remaining work:',
		);
		foreach ( $remaining_work as $item ) {
			$control_lines[] = '- ' . $item;
		}
		$open_requirements = self::normalize_text_list( $open_requirements, 8, 500 );
		if ( ! empty( $open_requirements ) ) {
			$control_lines[] = 'Open execution requirements:';
			$control_lines   = array_merge( $control_lines, $open_requirements );
		}

		$snapshot['messages'][] = array(
			'role'                 => 'system',
			'content'              => implode( "\n", $control_lines ),
			'ephemeral'            => true,
			'niwa_runtime_control' => true,
		);
		$snapshot['continuation_reason'] = 'task_checkpoint_continue';
		$snapshot['remaining_work']      = $remaining_work;
		$snapshot['task_checkpoint']     = $result['task_checkpoint'];

		$result['success']                 = false;
		$result['cancelled']               = true;
		$result['cancelled_reason']        = 'task_checkpoint_continue';
		$result['reply']                   = '';
		$result['error']                   = 'Task checkpoint requested durable continuation.';
		$result['context_snapshot']        = $snapshot;
		$result['task_completion_state']   = 'continuing';
		$result['original_task_completed'] = false;
		$result['retry_original_task']     = true;
		return $result;
	}

	private static function normalize_blocker( $blocker ) {
		$blocker = is_array( $blocker ) ? $blocker : array();
		$kind    = sanitize_key( (string) ( $blocker['kind'] ?? '' ) );
		if ( ! in_array(
			$kind,
			array(
				'external_dependency',
				'missing_founder_input',
				'authorization_required',
				'unsafe_without_approval',
				'external_service_unavailable',
			),
			true
		) ) {
			return array();
		}

		$description      = self::truncate_line( (string) ( $blocker['description'] ?? '' ), 500 );
		$evidence_call_id = sanitize_text_field( (string) ( $blocker['evidence_call_id'] ?? '' ) );
		if ( '' === $description || '' === $evidence_call_id ) {
			return array();
		}

		return array(
			'kind'             => $kind,
			'description'      => $description,
			'evidence_call_id' => $evidence_call_id,
		);
	}

	private static function normalize_text_list( $items, $limit, $text_limit ) {
		$normalized = array();
		foreach ( is_array( $items ) ? $items : array() as $item ) {
			if ( ! is_scalar( $item ) ) {
				continue;
			}
			$item = self::truncate_line( (string) $item, $text_limit );
			if ( '' !== $item ) {
				$normalized[] = $item;
			}
			if ( count( $normalized ) >= max( 1, intval( $limit ) ) ) {
				break;
			}
		}

		return array_values( array_unique( $normalized ) );
	}

	private static function truncate_line( $text, $limit ) {
		$text  = trim( preg_replace( '/\s+/', ' ', str_replace( "\0", '', (string) $text ) ) );
		$limit = max( 1, intval( $limit ) );
		if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_substr' ) ) {
			return mb_strlen( $text, 'UTF-8' ) > $limit
				? mb_substr( $text, 0, $limit, 'UTF-8' )
				: $text;
		}

		return strlen( $text ) > $limit ? substr( $text, 0, $limit ) : $text;
	}

	private static function truncate_reply( $text, $limit ) {
		$text  = trim( str_replace( "\0", '', (string) $text ) );
		$limit = max( 1, intval( $limit ) );
		if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_substr' ) ) {
			return mb_strlen( $text, 'UTF-8' ) > $limit
				? mb_substr( $text, 0, $limit, 'UTF-8' )
				: $text;
		}

		return strlen( $text ) > $limit ? substr( $text, 0, $limit ) : $text;
	}

	private static function error( $message ) {
		return array(
			'ok'      => false,
			'success' => false,
			'error'   => (string) $message,
		);
	}
}
