<?php
/**
 * Turn progress callback orchestration.
 *
 * @package Niwa_AI
 */

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

class Niwa_AI_Admin_Autonomy_Turn_Progress_Callback {
	/** @var array<string,callable> */
	private $callbacks;

	public function __construct( $callbacks = array() ) {
		$this->callbacks = is_array( $callbacks ) ? $callbacks : array();
	}

	public function build( $chat_id, $message_id = 0 ) {
		$chat_id = trim( (string) $chat_id );
		if ( '' === $chat_id ) {
			return null;
		}

		$message_id = intval( $message_id );

		return function ( $event = '', $payload = array() ) use ( $chat_id, $message_id ) {
			$this->handle( $chat_id, $message_id, $event, is_array( $payload ) ? $payload : array() );
		};
	}

	public function handle( $chat_id, $message_id, $event = '', $payload = array() ) {
		$event      = (string) $event;
		$payload    = is_array( $payload ) ? $payload : array();
		$chat_id    = trim( (string) $chat_id );
		$message_id = intval( $message_id );

		if ( $this->should_refresh_typing_feedback( $event, $payload ) ) {
			$this->call( 'keep_typing_feedback', array( $chat_id ) );
		}

		if ( function_exists( 'set_time_limit' ) ) {
			@set_time_limit( 200 );
		}

		if ( 'model_request' === $event ) {
			$this->debug(
				'turn_progress_model_request',
				array(
					'chat_id'    => $chat_id,
					'message_id' => $message_id,
					'payload'    => $this->sanitize_model_request_payload( $payload ),
				)
			);
		}

		$state = $this->call( 'get_turn_stream_state', array( $chat_id, $message_id ), array() );
		if ( empty( $state ) || ! is_array( $state ) ) {
			return;
		}

		$state['last_event_at'] = time();
		if (
			'message_delta' === $event
			&& empty( $state['streaming_disabled'] )
			&& empty( $state['assistant_stream_locked'] )
			&& ! empty( $payload['delta'] )
			&& is_string( $payload['delta'] )
		) {
			$state = $this->handle_message_delta( $state, $chat_id, $message_id, $payload );
		} elseif ( 'turn_ended' === $event ) {
			$state = $this->handle_turn_ended( $state, $chat_id, $message_id );
		} elseif ( 'tool_call_started' === $event ) {
			$state = $this->handle_tool_started( $state, $chat_id, $message_id, $payload );
		} elseif ( 'tool_call_finished' === $event ) {
			$this->debug(
				'turn_progress_tool_finished',
				array(
					'chat_id'    => $chat_id,
					'message_id' => $message_id,
					'tool'       => $this->payload_tool_name( $payload ),
				)
			);
			$state = $this->call( 'delete_turn_tool_status_message', array( $state, $payload, 'finished' ), $state );
		} elseif ( 'error' === $event ) {
			$this->debug(
				'turn_progress_error',
				array(
					'chat_id'    => $chat_id,
					'message_id' => $message_id,
					'payload'    => $payload,
				)
			);
			$state = $this->call( 'delete_all_turn_tool_status_messages', array( $state, 'error' ), $state );
		}

		$this->call( 'update_turn_stream_state', array( $chat_id, $message_id, $state ) );
	}

	private function handle_message_delta( $state, $chat_id, $message_id, $payload ) {
		$state['buffer'] = isset( $state['buffer'] )
			? (string) $state['buffer'] . (string) $payload['delta']
			: (string) $payload['delta'];

		if ( empty( $state['sent_any'] ) ) {
			$quote_directive = $this->call( 'consume_telegram_quote_current_directive', array( $state['buffer'] ), array( 'text' => $state['buffer'] ) );
			$state['buffer'] = isset( $quote_directive['text'] ) ? (string) $quote_directive['text'] : '';
			if ( ! empty( $quote_directive['quote_current'] ) ) {
				$state['quote_current_message'] = true;
			}
		}

		$this->debug(
			'turn_progress_message_delta',
			array(
				'chat_id'    => $chat_id,
				'message_id' => $message_id,
				'delta_len'  => $this->text_length( $payload['delta'] ),
				'buffer_len' => $this->text_length( $state['buffer'] ),
				'sent_chars' => intval( isset( $state['sent_chars'] ) ? $state['sent_chars'] : 0 ),
				'sent_any'   => ! empty( $state['sent_any'] ),
			)
		);

		return $this->call( 'maybe_flush_turn_stream_buffer', array( $state, false ), $state );
	}

	private function handle_turn_ended( $state, $chat_id, $message_id ) {
		$this->debug(
			'turn_progress_turn_ended',
			array(
				'chat_id'    => $chat_id,
				'message_id' => $message_id,
				'buffer_len' => $this->text_length( isset( $state['buffer'] ) ? $state['buffer'] : '' ),
				'sent_chars' => intval( isset( $state['sent_chars'] ) ? $state['sent_chars'] : 0 ),
			)
		);
		$state = $this->call( 'delete_all_turn_tool_status_messages', array( $state, 'turn_ended' ), $state );

		return $this->call( 'maybe_flush_turn_stream_buffer', array( $state, true ), $state );
	}

	private function handle_tool_started( $state, $chat_id, $message_id, $payload ) {
		$tool = $this->payload_tool_name( $payload );
		$this->debug(
			'turn_progress_tool_started',
			array(
				'chat_id'    => $chat_id,
				'message_id' => $message_id,
				'tool'       => $tool,
			)
		);

		if ( empty( $state['assistant_stream_locked'] ) ) {
			$state['assistant_stream_locked'] = true;
			$state['tools_used']             = true;
			if ( ! empty( $state['buffer'] ) ) {
				$this->debug(
					'turn_stream_buffer_discarded_after_tool_start',
					array(
						'chat_id'    => $chat_id,
						'message_id' => $message_id,
						'tool'       => $tool,
						'buffer_len' => $this->text_length( (string) $state['buffer'] ),
					)
				);
				$state['buffer'] = '';
			}
		}

		return $this->call( 'maybe_send_turn_tool_status', array( $state, 'start', $payload ), $state );
	}

	private function payload_tool_name( $payload ) {
		if ( isset( $payload['tool'] ) ) {
			return (string) $payload['tool'];
		}
		if ( isset( $payload['name'] ) ) {
			return (string) $payload['name'];
		}

		return '';
	}

	private function text_length( $text ) {
		return intval( $this->call( 'text_length', array( $text ), strlen( (string) $text ) ) );
	}

	private function should_refresh_typing_feedback( $event, $payload ) {
		if ( 'turn_ended' === $event ) {
			return false;
		}

		if (
			! empty( $payload['after_reply'] )
			|| ! empty( $payload['post_reply'] )
			|| ! empty( $payload['postprocess'] )
		) {
			return false;
		}

		return true;
	}

	private function sanitize_model_request_payload( $payload ) {
		$payload = is_array( $payload ) ? $payload : array();
		$allowed = array(
			'stage',
			'timeout_seconds',
			'request_payload_bytes',
			'message_count',
			'tool_count',
			'elapsed_ms',
			'success',
			'http_status',
			'transport',
			'stop_reason',
			'tool_call_count',
			'events_seen',
			'bytes_read',
			'downloaded',
			'uploaded',
			'heartbeat_count',
			'index',
			'id',
			'name',
			'model_request_timing',
		);
		$out = array();
		foreach ( $allowed as $key ) {
			if ( ! array_key_exists( $key, $payload ) ) {
				continue;
			}
			if ( 'model_request_timing' === $key ) {
				$out[ $key ] = array();
				if ( is_array( $payload[ $key ] ) ) {
					foreach ( $payload[ $key ] as $timing_key => $timing_value ) {
						$out[ $key ][ sanitize_key( (string) $timing_key ) ] = is_scalar( $timing_value ) ? sanitize_text_field( (string) $timing_value ) : '';
					}
				}
				continue;
			}
			$out[ $key ] = is_scalar( $payload[ $key ] ) ? sanitize_text_field( (string) $payload[ $key ] ) : '';
		}

		return $out;
	}

	private function debug( $event, $context = array() ) {
		$this->call( 'debug_logger', array( $event, $context ) );
	}

	private function call( $name, $args = array(), $default = null ) {
		return Niwa_AI_Admin_Autonomy_Callbacks::call( $this->callbacks, $name, $args, $default );
	}
}
