<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Data_Objects\Job;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Factories\Job_Handler_Factory;
use Limb_Chatbot\Includes\Repositories\Job_Repository;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Jobs\Job_Service;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Jobs REST API Controller
 *
 * Handles HTTP concerns for job queue endpoints.
 * Delegates business logic to service layer (SOLID principles).
 *
 * @since 1.0.9
 */
class Jobs_Controller extends Rest_Controller {

	/**
	 * Resource name for this controller.
	 *
	 * @var string
	 * @since 1.0.9
	 */
	protected $rest_base = 'jobs';

	private Job_Service $service;

	private Job_Repository $repository;

	/**
	 * Constructor.
	 *
	 * @since 1.0.9
	 */
	public function __construct() {
		$this->service    = new Job_Service();
		$this->repository = new Job_Repository();
	}

	/**
	 * Register REST API routes.
	 *
	 * @return void
	 * @since 1.0.9
	 */
	public function register_routes(): void {
		// Get jobs collection / Create job
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_items' ],
					'permission_callback' => [ $this, 'permission_callback' ],
					'args'                => $this->get_collection_params(),
				],
				[
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => [ $this, 'create_item' ],
					'permission_callback' => [ $this, 'permission_callback' ],
					'args'                => $this->get_create_params(),
				],
			]
		);

		// Get job (with stats)
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_item' ],
					'permission_callback' => [ $this, 'permission_callback' ],
					'args'                => [
						'id' => [
							'description' => __( 'Job ID', 'limb-chatbot' ),
							'type'        => 'integer',
							'required'    => true,
						],
					],
				],
			]
		);

		// Pause job
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)/pause',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'pause_job' ],
				'permission_callback' => [ $this, 'permission_callback' ],
				'args'                => [
					'id' => [
						'description' => __( 'Job ID', 'limb-chatbot' ),
						'type'        => 'integer',
						'required'    => true,
					],
				],
			]
		);

		// Resume job
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)/resume',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'resume_job' ],
				'permission_callback' => [ $this, 'permission_callback' ],
				'args'                => [
					'id' => [
						'description' => __( 'Job ID', 'limb-chatbot' ),
						'type'        => 'integer',
						'required'    => true,
					],
				],
			]
		);

		// Cancel job (deletes job and all tasks)
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)/cancel',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'cancel_job' ],
				'permission_callback' => [ $this, 'permission_callback' ],
				'args'                => [
					'id' => [
						'description' => __( 'Job ID', 'limb-chatbot' ),
						'type'        => 'integer',
						'required'    => true,
					],
				],
			]
		);

		// Process batch endpoint (frontend-driven)
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)/process',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'process_batch' ],
				'permission_callback' => [ $this, 'permission_callback' ],
				'args'                => [
					'id' => [
						'description' => __( 'Job ID', 'limb-chatbot' ),
						'type'        => 'integer',
						'required'    => true,
					],
				],
			]
		);
	}

	/**
	 * Returns query parameters for the collection endpoint.
	 *
	 * Supports filtering by type, sub_type, status, and chatbot_uuid,
	 * along with pagination and ordering.
	 *
	 * @return array Array of query parameters accepted by the collection route.
	 * @since 1.1.0
	 */
	public function get_collection_params() {
		return array_merge( parent::get_collection_params(), [
			'type'         => [
				'description'       => __( 'Filter jobs by type.', 'limb-chatbot' ),
				'type'              => 'string',
				'required'          => false,
				'sanitize_callback' => 'sanitize_text_field',
			],
			'sub_type'     => [
				'description'       => __( 'Filter jobs by sub-type.', 'limb-chatbot' ),
				'type'              => 'string',
				'required'          => false,
				'sanitize_callback' => 'sanitize_text_field',
			],
			'status'        => [
				'description'       => __( 'Filter jobs by status.', 'limb-chatbot' ),
				'type'              => 'string',
				'required'          => false,
				'enum'              => [
					Job::STATUS_PENDING,
					Job::STATUS_GENERATING_TASKS,
					Job::STATUS_PROCESSING,
					Job::STATUS_PAUSED,
					Job::STATUS_COMPLETED,
					Job::STATUS_FAILED,
					Job::STATUS_CANCELLED,
				],
				'sanitize_callback' => 'sanitize_text_field',
			],
			'chatbot_uuid'  => [
				'description'       => __( 'Filter jobs by chatbot UUID. Use empty string for default chatbot.', 'limb-chatbot' ),
				'type'              => 'string',
				'required'          => false,
				'sanitize_callback' => 'sanitize_key',
			],
		] );
	}

	/**
	 * Get parameters for job creation.
	 *
	 * @return array Parameter definitions.
	 * @since 1.0.9
	 */
	private function get_create_params(): array {
		return [
			'type'         => [
				'description'       => __( 'Job type', 'limb-chatbot' ),
				'type'              => 'string',
				'required'          => true,
				'validate_callback' => function ( $value ) {
					if ( ! is_string( $value ) || empty( $value ) ) {
						return new WP_Error(
							'invalid_job_type',
							__( 'Job type must be a non-empty string.', 'limb-chatbot' )
						);
					}

					return ( new Job_Handler_Factory() )->has_handler( $value );
				},
				'sanitize_callback' => 'sanitize_text_field',
			],
			'config'       => [
				'description'       => __( 'Job configuration', 'limb-chatbot' ),
				'type'              => 'object',
				'required'          => false,
				'default'           => [],
				'validate_callback' => 'rest_validate_request_arg',
			],
			'chatbot_uuid' => [
				'description'       => __( 'Job configuration', 'limb-chatbot' ),
				'type'              => 'object',
				'required'          => false,
				'default'           => [],
				'sanitize_callback' => 'sanitize_key',
				'validate_callback' => function ( $value ) {
					return empty( $value ) || $value === Job::CHATBOT_DEFAULT || Chatbot::find_by_uuid( $value ) instanceof Chatbot;
				},
			],
			'sub_type'     => [
				'description'       => __( 'Job sub-type', 'limb-chatbot' ),
				'type'              => 'string',
				'required'          => false,
				'default'           => null,
				'sanitize_callback' => 'sanitize_text_field',
			],
		];
	}

	/**
	 * Create a new job and trigger background processing.
	 *
	 * @param  WP_REST_Request  $request  Request object.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.9
	 */
	public function create_item( $request ) {
		try {
			$type         = $request->get_param( 'type' );
			$config       = $request->get_param( 'config' ) ?? [];
			$chatbot_uuid = $request->get_param( 'chatbot_uuid' ) ?? null;
			$sub_type     = $request->get_param( 'sub_type' ) ?? null;

			$job = $this->service->create_job( $type, $config, $chatbot_uuid, $sub_type );

			return rest_ensure_response( $job );
		} catch ( \Exception $e ) {
			Helper::log( $e );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Returns a collection of job items.
	 *
	 * Supports filtering by type, sub_type, status, and chatbot_uuid,
	 * along with pagination and ordering.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.1.0
	 */
	public function get_items( $request ) {
		try {
			$jobs = $this->repository->get_items( $request->get_query_params() );
			$items = $this->prepare_collection( $jobs, $request );

			return rest_ensure_response( $items ?? [] );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Get a job with stats (stats only, no processing).
	 *
	 * @param  WP_REST_Request  $request  Request object.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.9
	 */
	public function get_item( $request ) {
		try {
			$job_id = (int) $request->get_param( 'id' );

			$job = $this->repository->find( $job_id );

			if ( ! $job ) {
				return rest_ensure_response( [
					'completed' => true,
					'message'   => __( 'Job completed successfully and has been removed.', 'limb-chatbot' ),
				] );
			}

			// Snapshot data BEFORE mutation
			$response = $job->to_array();

			// Clear errors for next request
			$job->errors = [];
			$job->save();

			return rest_ensure_response( $response );
		} catch ( \Exception $e ) {
			Helper::log( $e );
			return Helper::get_wp_error( $e );
		}
	}



	/**
	 * Pause a job.
	 *
	 * @param  WP_REST_Request  $request  Request object.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.9
	 */
	public function pause_job( $request ) {
		try {
			$job_id = (int) $request->get_param( 'id' );

			$job = $this->service->pause_job( $job_id );

			return rest_ensure_response( $job );
		} catch ( \Exception $e ) {
			Helper::log( $e );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Resume a job and trigger background processing.
	 *
	 * @param  WP_REST_Request  $request  Request object.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.9
	 */
	public function resume_job( $request ) {
		try {
			$job_id = (int) $request->get_param( 'id' );

			$job = $this->service->resume_job( $job_id );

			return rest_ensure_response( $job );
		} catch ( \Exception $e ) {
			Helper::log( $e );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Cancel a job and delete all associated data.
	 *
	 * @param  WP_REST_Request  $request  Request object.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.9
	 */
	public function cancel_job( $request ) {
		try {
			$job_id  = (int) $request->get_param( 'id' );
			$deleted = $this->service->cancel_job( $job_id );

			if ( ! $deleted ) {
				throw new Exception(
					Error_Codes::TECHNICAL_ERROR,
					__( 'Failed to cancel job', 'limb-chatbot' )
				);
			}

			return rest_ensure_response( [ 'cancelled' => true, 'id' => $job_id ] );
		} catch ( \Exception $e ) {
			Helper::log( $e );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Process a batch of tasks or generate tasks for a job.
	 *
	 * Handles two phases:
	 * 1. Task Generation (if status is generating_tasks)
	 * 2. Task Processing (if status is processing)
	 *
	 * Controller responsibility: HTTP concerns only
	 * Business logic delegated to service layer
	 *
	 * @param  WP_REST_Request  $request  Request object.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.9
	 */
	public function process_batch( $request ) {
		try {
			$job_id = (int) $request->get_param( 'id' );
			$job    = $this->repository->find( $job_id );
			if ( ! $job ) {
				throw new Exception( Error_Codes::NOT_FOUND, __( 'Job not found.', 'limb-chatbot' ) );
			}
			if ( ! $job->can_process() ) {
				throw new Exception(
					Error_Codes::VALIDATION_INVALID_VALUE,
					sprintf( __( 'Job cannot be processed (status: %s)', 'limb-chatbot' ), $job->get_status() )
				);
			}
			// Handle task generation phase
			if ( $job->is_generating_tasks() || $job->is_pending() ) {
				$job = $this->service->generate_job_batch( $job, 100 );

				return rest_ensure_response( $job );
			}
			// Handle task processing phase
			if ( $job->is_processing() ) {
				$job = $this->service->process_batch( $job );

				// If job is null, it was completed and deleted
				if ( ! $job ) {
					return rest_ensure_response( [
						'completed' => true,
						'message'   => __( 'Job completed successfully and has been removed.', 'limb-chatbot' ),
					] );
				}

				return rest_ensure_response( $job );
			}

			// Job is in an unexpected state
			return rest_ensure_response( $job );
		} catch ( \Exception $e ) {
			Helper::log( $e );

			return Helper::get_wp_error( $e );
		}
	}
}

