<?php
namespace ZenCommunity\Database\Models;
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
use WP_User;
use ZenCommunity\Helper;
use ZenCommunity\Database\Utils\QueryBuilder;
use ZenCommunity\Classes\Sanitizer;
use ZenCommunity\Exceptions\ZencommunityException;
use ZenCommunity\Database\Utils\Model;
class ProcessLog extends Model {
	protected string $table = 'zenc_process_logs';
	protected ?string $alias = 'p';
	protected array   $selects = [
		'p.*'
	];

	public static function remove( string $pid ) : bool {
		return static::ins()->qb()
			->where( 'p.pid', '=', $pid )
			->delete();
	}

	public static function exists( string $pid ) : bool {
		return static::ins()->qb()
			->where( 'p.pid', '=', $pid )
			->count() > 0;
	}

	public static function wrapper( array $product ) : array {
		$product['args'] 	  = json_decode( $product['args'] ?? '{}', true );
		return $product;
	}

	public static function collection_wrapper( array $products ) : array {
		$data = [];
		foreach ( $products as $product ) {
			$data[] = static::wrapper( $product );
		}
		return $data;
	}

    public static function index( array $params ) : array {
        $qb = static::ins()->qb();
        
		$rules = apply_filters( "zencommunity/ai_agent/msgs/index/sanitization_rules",  [
			'page' 	   => Sanitizer::INT,
			'per_page' => Sanitizer::INT,
			'search'   => Sanitizer::STRING,
			'handler'   => Sanitizer::STRING,
			'order'    => Sanitizer::STRING,
			'order_by' => Sanitizer::STRING,
		] );

		$params = Sanitizer::sanitize( $params, $rules );
		$params = apply_filters( "zencommunity/ai_agent/msgs/index/query", $params );

        $page 	  = $params['page'] ?? 0;
        $per_page = $params['per_page'] ?? 15;
        $search   = $params['search'] ?? '';
        $handler   = $params['handler'] ?? '';
        $order    = $params['order'] ?? 'desc';
        $order_by = $params['order_by'] ?? 'id';
        
        if ( ! empty( $per_page ) && $per_page > 100 ) {
			throw new ZencommunityException( esc_html( __( 'The maximum value of  per_page is 100.', 'zencommunity-pro' ) ) );
        }
        
        if ( ! empty( $search ) ) {
            $qb->where( 'p.handler', 'LIKE', "%{$search}%" );
        }


        if ( ! empty( $handler ) ) {
            $qb->where( 'p.handler', '=', $handler );
        }

        if ( ! in_array( $order = strtoupper( $order ), [ 'ASC', 'DESC' ] ) ) {
            throw new ZencommunityException( esc_html( __( 'invalid order.', 'zencommunity-pro' ) ) );
		}

        if ( ! in_array( $order_by, [ 'created_at', 'updated_at', 'id' ], true ) ) {
            throw new ZencommunityException( esc_html( __( 'invalid order column.', 'zencommunity-pro' ) ) );
        }

		$order_by = 'p.' . $order_by;
		$qb->order_by( $order_by, $order );
		
        $data = $qb->paginate( $page, $per_page );
		$data['records'] = static::collection_wrapper( $data['records'] ?? [] );
		return $data;
    }
	
	public static function create( 
		string $handler, 
		array $args,
		int $max_retry = 5,
	) : ?string {
		$data = [];
        $data['pid'] = uniqid('', true);
        $data['handler'] = $handler;
        $data['max_retry'] = $max_retry;
        $data['args'] = json_encode( $args );
        $data['created_at'] = time();
		if( empty( $id = static::ins()->insert( $data ) ) ) 
			return null;
		return static::ins()->qb()
			->where( 'p.id', '=', $id )
			->value( 'p.pid' );
	}

	public static function update( string $pid, array $data ) : bool {
        if (  empty( $pid ) )
			throw new ZencommunityException( esc_html( __( 'PID is empty.', 'zencommunity' ) ) );

        if (  empty( $data ) )
			throw new ZencommunityException( esc_html( __( 'data is empty.', 'zencommunity' ) ) );

        if ( ! static::exists( $pid ) )
			throw new ZencommunityException( esc_html( __( 'Process is not exists.', 'zencommunity' ) ) );

        $data['cupdated_at'] = time();
		return static::ins()->qb()
			->where( 'p.pid', '=', $pid )
			->update( $data );
	}

	public static function dispatch( string $pid ) : bool {
        if (  empty( $pid ) )
			throw new ZencommunityException( esc_html( __( 'PID is empty.', 'zencommunity' ) ) );

		$data = static::ins()->qb()
			->select( [ 'p.status', 'p.handler', 'p.args', 'p.max_retry', 'p.retry', 'p.updated_at' ] )
			->where( 'p.pid', '=', $pid )
			->first();

        if ( empty( $data ) )
			throw new ZencommunityException( esc_html( __( 'Process is not exists.', 'zencommunity' ) ) );

        if ( 
			120 > ( time() - $data['updated_at'] ) &&
			'running' === $data['status'] 
		)
			throw new ZencommunityException( esc_html( __( 'Process is already running.', 'zencommunity' ) ) );

		$handler = $data['handler'];
		try{
			static::ins()->qb()
				->where( 'p.pid', '=', $pid )
				->update( [ 
					'status' => 'running',
					'updated_at' => time()
				] );

			if ( ! has_action( $action = 'zencommunity/bg_process/' . $handler ) )
				throw new ZencommunityException( esc_html( __( 'Handler is not exists.', 'zencommunity' ) ) );

			do_action( $action, json_decode( $data['args'], true ) );

			static::remove( $pid );
			return true;
		}
		catch( \Throwable $e ) {
			if( intval( $data['retry'] ) >=  intval( $data['max_retry'] ) ) {
				static::remove( $pid );
				return false;
			}
			$data_to_update = [];
			$data_to_update['status'] = 'failed';
			$data_to_update['retry'] = $data['retry'] + 1;
			$data_to_update['error_msg'] = esc_html( $e->getMessage() );
			static::ins()->qb()
				->where( 'p.pid', '=', $pid )
				->update( $data_to_update );
			return false;
		}

		return true;
	}

    public static function run( string $pid ) : void {
        if (  empty( $pid ) )
			throw new ZencommunityException( esc_html( __( 'PID is empty.', 'zencommunity' ) ) );
		
        if ( ! static::exists( $pid ) )
			throw new ZencommunityException( esc_html( __( 'Process is not exists.', 'zencommunity' ) ) );

        $endpoint_url = rest_url( "zencommunity/v1/bg/process/{$pid}"); 
        $response = wp_remote_get( $endpoint_url, [
            'sslverify' => false,
            'blocking'  => false,
            'timeout'   => 1,
        ] );
    }
}