<?php

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

class Niwa_AI_Admin_Autonomy_Typing_Feedback {

    /** @var Niwa_AI_Admin_Autonomy_Telegram|null */
    private $telegram;

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

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

    public function begin( $chat_id ) {
        $chat_id = trim( (string) $chat_id );
        if ( '' === $chat_id || ! $this->can_send_indicator( $chat_id ) ) {
            return;
        }

        $state                 = $this->get_state( $chat_id );
        $state['chat_id']      = $chat_id;
        $state['token']        = ! empty( $state['token'] ) ? (string) $state['token'] : wp_generate_password( 24, false, false );
        $state['active_until'] = time() + 75;
        $state['last_touch']   = time();
        $this->update_state( $chat_id, $state );
        $this->send_indicator( $chat_id );

        if ( empty( $state['running'] ) || intval( isset( $state['last_ping'] ) ? $state['last_ping'] : 0 ) < ( time() - 8 ) ) {
            $last_dispatch = isset( $state['last_dispatch'] ) ? intval( $state['last_dispatch'] ) : 0;
            if ( ( time() - $last_dispatch ) >= 25 ) {
                $state['last_dispatch'] = time();
                $this->update_state( $chat_id, $state );
                $this->dispatch_runner( $chat_id, $state['token'] );
            }
        }
    }

    public function keep( $chat_id ) {
        $chat_id = trim( (string) $chat_id );
        if ( '' === $chat_id || ! $this->can_send_indicator( $chat_id ) ) {
            return;
        }

        $state = $this->get_state( $chat_id );
        if ( empty( $state['token'] ) ) {
            $this->begin( $chat_id );
            return;
        }

        $now                   = time();
        $state['active_until'] = $now + 75;
        $state['last_touch']   = $now;

        $last_direct = isset( $state['last_direct_send'] ) ? intval( $state['last_direct_send'] ) : 0;
        if ( ( $now - $last_direct ) >= 4 ) {
            $this->send_indicator( $chat_id );
            $state['last_direct_send'] = $now;
        }

        $this->update_state( $chat_id, $state );

        if ( empty( $state['running'] ) || intval( isset( $state['last_ping'] ) ? $state['last_ping'] : 0 ) < ( $now - 8 ) ) {
            $last_dispatch = isset( $state['last_dispatch'] ) ? intval( $state['last_dispatch'] ) : 0;
            if ( ( $now - $last_dispatch ) >= 25 ) {
                $state['last_dispatch'] = $now;
                $this->update_state( $chat_id, $state );
                $this->dispatch_runner( $chat_id, $state['token'] );
            }
        }
    }

    public function end( $chat_id ) {
        $chat_id = trim( (string) $chat_id );
        if ( '' === $chat_id ) {
            return;
        }

        $state = $this->get_state( $chat_id );
        if ( empty( $state ) ) {
            return;
        }

        $state['active_until'] = 0;
        $state['last_touch']   = time();
        $this->update_state( $chat_id, $state );
    }

    public function handle_web_runner() {
        ignore_user_abort( true );
        if ( function_exists( 'set_time_limit' ) ) {
            @set_time_limit( 35 );
        }

        $chat_id = isset( $_REQUEST['chat_id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['chat_id'] ) ) : '';
        $token   = isset( $_REQUEST['typing_key'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['typing_key'] ) ) : '';
        $state   = $this->get_state( $chat_id );

        $sig_valid = $this->verify_signature( 'niwa_typing', $token );
        if ( ! $sig_valid ) {
            if ( '' === $chat_id || '' === $token || empty( $state['token'] ) || ! hash_equals( (string) $state['token'], $token ) ) {
                status_header( 403 );
                echo 'forbidden';
                exit;
            }
        }

        $this->run_loop( $chat_id, $token );

        status_header( 200 );
        echo 'ok';
        exit;
    }

    public function run_cli( $chat_id = '', $token = '' ) {
        $chat_id = trim( (string) $chat_id );
        $token   = trim( (string) $token );
        $state   = $this->get_state( $chat_id );

        if ( '' === $chat_id || '' === $token || empty( $state['token'] ) || ! hash_equals( (string) $state['token'], $token ) ) {
            return array(
                'success'   => false,
                'error'     => 'invalid_typing_token',
                'transport' => 'local_cli',
            );
        }

        return $this->run_loop( $chat_id, $token, 'local_cli' );
    }

    public function send_from_run( $run, $is_realtime ) {
        if ( empty( $run ) || ! is_array( $run ) || empty( $is_realtime ) ) {
            return;
        }

        $context = isset( $run['context'] ) && is_array( $run['context'] ) ? $run['context'] : array();
        $chat_id = isset( $context['chat_id'] ) ? trim( (string) $context['chat_id'] ) : '';
        if ( '' !== $chat_id ) {
            $this->send_indicator( $chat_id );
        }
    }

    private function run_loop( $chat_id, $token, $transport = 'web_loopback' ) {
        $chat_id = trim( (string) $chat_id );
        $token   = trim( (string) $token );
        if ( '' === $chat_id || '' === $token ) {
            return array(
                'success'   => false,
                'error'     => 'invalid_typing_request',
                'transport' => $transport,
            );
        }

        $state              = $this->get_state( $chat_id );
        $state['running']   = true;
        $state['last_ping'] = time();
        $state['transport'] = $transport;
        $this->update_state( $chat_id, $state );

        $deadline = time() + 30;
        while ( time() < $deadline ) {
            $state = $this->get_state( $chat_id );
            if ( empty( $state['token'] ) || ! hash_equals( (string) $state['token'], $token ) ) {
                break;
            }
            if ( intval( isset( $state['active_until'] ) ? $state['active_until'] : 0 ) < time() ) {
                break;
            }

            $state['running']   = true;
            $state['last_ping'] = time();
            $state['transport'] = $transport;
            $this->update_state( $chat_id, $state );
            $this->send_indicator( $chat_id );
            sleep( 5 );
        }

        $state = $this->get_state( $chat_id );
        if ( ! empty( $state['token'] ) && hash_equals( (string) $state['token'], $token ) ) {
            $state['running']   = false;
            $state['last_ping'] = time();
            $state['transport'] = $transport;
            $this->update_state( $chat_id, $state );
        }

        return array(
            'success'   => true,
            'transport' => $transport,
        );
    }

    private function dispatch_runner( $chat_id, $token ) {
        $chat_id = trim( (string) $chat_id );
        $token   = trim( (string) $token );
        if ( '' === $chat_id || '' === $token ) {
            return;
        }

        if ( $this->should_use_local_cli() ) {
            $local_cli = $this->dispatch_local_cli_runner( $chat_id, $token );
            $this->debug(
                empty( $local_cli['success'] ) ? 'typing_runner_dispatch_failed' : 'typing_runner_dispatched',
                array(
                    'transport' => 'local_cli',
                    'error'     => isset( $local_cli['error'] ) ? (string) $local_cli['error'] : '',
                )
            );
            if ( ! empty( $local_cli['success'] ) ) {
                return;
            }

            $this->debug(
                'typing_runner_local_cli_fallback',
                array(
                    'transport' => 'web_loopback',
                    'error'     => isset( $local_cli['error'] ) ? (string) $local_cli['error'] : 'local_cli_dispatch_failed',
                )
            );
        }

        $body = $this->build_signature_body( 'niwa_typing', $token );
        $body['chat_id']    = $chat_id;
        $body['typing_key'] = $token;

        wp_remote_post(
            add_query_arg(
                array(
                    'action'  => 'niwa_ai_admin_autonomy_typing',
                    'chat_id' => $chat_id,
                ),
                admin_url( 'admin-post.php' )
            ),
            array(
                'timeout'   => 1,
                'blocking'  => false,
				'sslverify' => true,
                'body'      => $body,
            )
        );
    }

    private function dispatch_local_cli_runner( $chat_id, $token ) {
        if ( ! class_exists( 'Niwa_AI_Admin_Autonomy_Process_Runtime' ) || ! defined( 'NIWA_AI_DIR' ) ) {
            return array(
                'success' => false,
                'error'   => 'local_cli_unavailable',
            );
        }

        return Niwa_AI_Admin_Autonomy_Process_Runtime::spawn_background_php(
            NIWA_AI_DIR . 'bin/typing-runner.php',
            array(
                'chat-id' => (string) $chat_id,
                'token'   => (string) $token,
            ),
            defined( 'ABSPATH' ) ? ABSPATH : null
        );
    }

    private function send_indicator( $chat_id ) {
        if ( isset( $this->callbacks['send_typing_indicator'] ) && is_callable( $this->callbacks['send_typing_indicator'] ) ) {
            $this->call( 'send_typing_indicator', false, array( $chat_id ) );
            return;
        }

        if ( $this->telegram && method_exists( $this->telegram, 'send_chat_action' ) ) {
            $this->telegram->send_chat_action( $chat_id, 'typing' );
        }
    }

    private function can_send_indicator( $chat_id ) {
        if ( isset( $this->callbacks['send_typing_indicator'] ) && is_callable( $this->callbacks['send_typing_indicator'] ) ) {
            return true;
        }

        return $this->telegram && method_exists( $this->telegram, 'send_chat_action' );
    }

    private function get_state( $chat_id ) {
        $state = Niwa_AI_Durable_Cache::get( $this->state_key( $chat_id ) );
        return is_array( $state ) ? $state : array();
    }

    private function update_state( $chat_id, $state ) {
        Niwa_AI_Durable_Cache::set( $this->state_key( $chat_id ), is_array( $state ) ? $state : array(), 2 * MINUTE_IN_SECONDS );
    }

    private function state_key( $chat_id ) {
        return 'niwa_ai_admin_typing_' . md5( (string) $chat_id );
    }

    private function should_use_local_cli() {
        return ! empty( $this->call( 'should_use_local_cli_runner', false ) );
    }

    private function build_signature_body( $prefix, $secret ) {
        return (array) $this->call( 'build_loopback_signature_body', array(), array( $prefix, $secret ) );
    }

    private function verify_signature( $prefix, $secret ) {
        return ! empty( $this->call( 'verify_loopback_signature', false, array( $prefix, $secret ) ) );
    }

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

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