<?php

namespace Limb_Chatbot\Includes\Repositories;

use Limb_Chatbot\Includes\Data_Objects\Job;
use Limb_Chatbot\Includes\Services\Data_Object_Collection;

/**
 * Repository for managing Job records.
 *
 * Provides methods to retrieve, create, update, and delete jobs.
 *
 * @since 1.0.9
 */
class Job_Repository {

	/**
	 * Create a new job.
	 *
	 * @param  array  $data  Job data.
	 *
	 * @return Job|null
	 * @throws \Exception
	 * @since 1.0.9
	 */
	public function create( array $data ): ?Job {
		$job = Job::create( $data );

		return $job ?: null;
	}

	/**
	 * Update an existing job.
	 *
	 * @param  Job  $job  Job object to update.
	 * @param  array  $data  Data to update.
	 *
	 * @return Job
	 * @since 1.0.9
	 */
	public function update( $job, array $data ): Job {
		if ( $job instanceof Job ) {
			$id = $job->get_id();
		} else {
			$id = $job;
		}
		// Always update the updated_at timestamp
		$data['updated_at'] = current_time( 'mysql', true );

		return $job->update( [ 'id' => $id ], $data );
	}

	/**
	 * Find a job by ID.
	 *
	 * @param int $job_id Job ID.
	 * @return Job|null
	 * @since 1.0.9
	 */
	public function find( int $job_id ): ?Job {
		return Job::find( $job_id );
	}

	/**
	 * Find jobs by criteria.
	 *
	 * @param array $where Where conditions.
	 * @param int $per_page Results per page.
	 * @param int $page Page number.
	 * @param string $orderby Order by field.
	 * @param string $order Order direction (ASC|DESC).
	 * @return Data_Object_Collection
	 * @since 1.0.9
	 */
	public function where( array $where, int $per_page = 10, int $page = 1, string $orderby = 'id', string $order = 'DESC' ): Data_Object_Collection {
		return Job::where( $where, $per_page, $page, $orderby, $order );
	}

	/**
	 * Get the first job matching criteria.
	 *
	 * @param array $where Where conditions.
	 * @return Job|null
	 * @since 1.0.9
	 */
	public function first( array $where ): ?Job {
		$results = $this->where( $where, 1, 1 );
		return $results->first();
	}

	/**
	 * Count jobs matching criteria.
	 *
	 * @param array $where Where conditions.
	 * @return int
	 * @since 1.0.9
	 */
	public function count( array $where = [] ): int {
		return Job::count( $where ) ?? 0;
	}

	/**
	 * Delete a job.
	 *
	 * @param Job $job Job to delete.
	 * @return bool
	 * @since 1.0.9
	 */
	public function delete( Job $job ): bool {
		return Job::delete( [ 'id' => $job->get_id() ] );
	}

	/**
	 * Delete a job by ID.
	 *
	 * @param int $job_id Job ID.
	 * @return bool
	 * @since 1.1.0
	 */
	public function delete_by_id( int $job_id ): bool {
		return Job::delete( [ 'id' => $job_id ] );
	}

	/**
	 * Get jobs by status.
	 *
	 * @param string $status Job status.
	 * @param int $limit Maximum number of jobs to retrieve.
	 * @return Data_Object_Collection
	 * @since 1.1.0
	 */
	public function get_by_status( string $status, int $limit = 10 ): Data_Object_Collection {
		return $this->where( [ 'status' => $status ], $limit, 1, 'created_at', 'ASC' );
	}

	/**
	 * Get jobs by type.
	 *
	 * @param string $type Job type.
	 * @param int $limit Maximum number of jobs to retrieve.
	 * @return Data_Object_Collection
	 * @since 1.1.0
	 */
	public function get_by_type( string $type, int $limit = 10 ): Data_Object_Collection {
		return $this->where( [ 'type' => $type ], $limit, 1, 'created_at', 'DESC' );
	}

	/**
	 * Get processing jobs.
	 *
	 * @param int $limit Maximum number of jobs to retrieve.
	 * @return Data_Object_Collection
	 * @since 1.1.0
	 */
	public function get_processing_jobs( int $limit = 10 ): Data_Object_Collection {
		return $this->get_by_status( Job::STATUS_PROCESSING, $limit );
	}

	/**
	 * Get pending jobs.
	 *
	 * @param int $limit Maximum number of jobs to retrieve.
	 * @return Data_Object_Collection
	 * @since 1.1.0
	 */
	public function get_pending_jobs( int $limit = 10 ): Data_Object_Collection {
		return $this->get_by_status( Job::STATUS_PENDING, $limit );
	}

	/**
	 * Check if a job exists by ID.
	 *
	 * @param int $job_id Job ID.
	 * @return bool
	 * @since 1.1.0
	 */
	public function exists( int $job_id ): bool {
		return $this->find( $job_id ) !== null;
	}

	/**
	 * Retrieves a collection of jobs based on given parameters.
	 *
	 * Supports filtering by type, sub_type, status, and chatbot_uuid, with pagination and ordering.
	 *
	 * @param  array  $params  {
	 *     Optional. Parameters for filtering and pagination.
	 *
	 *     @type string $type Job type to filter by.
	 *     @type string $sub_type Job sub-type to filter by.
	 *     @type string $status Job status to filter by.
	 *     @type string $chatbot_uuid Chatbot UUID to filter by. Empty string will be converted to null.
	 *     @type int $per_page Number of results per page. Default 10.
	 *     @type int $page Current page number. Default 1.
	 *     @type string $orderby Field to order by. Default 'created_at'.
	 *     @type string $order Sort order direction. Accepts 'asc' or 'desc'. Default 'desc'.
	 * }
	 *
	 * @return Data_Object_Collection Collection of Job objects.
	 * @since 1.1.0
	 */
	public function get_items( array $params = [] ): Data_Object_Collection {
		// Build where conditions from filter params
		$where = [];

		// Filter by type
		if ( ! empty( $params['type'] ) ) {
			$where['type'] = $params['type'];
		}

		// Filter by sub_type
		if ( ! empty( $params['sub_type'] ) ) {
			$where['sub_type'] = $params['sub_type'];
		}

		// Filter by status
		if ( ! empty( $params['status'] ) ) {
			$where['status'] = $params['status'];
		}

		// Filter by chatbot_uuid (handle empty string as null)
		if ( array_key_exists( 'chatbot_uuid', $params ) ) {
			if ( empty( $params['chatbot_uuid'] ) ) {
				$where['chatbot_uuid'] = null;
			} else {
				$where['chatbot_uuid'] = $params['chatbot_uuid'];
			}
		}

		// Handle ordering
		$order_by = $params['orderby'] ?? 'created_at';
		$order    = strtoupper( $params['order'] ?? 'DESC' );

		// Handle pagination
		$page     = (int) ( $params['page'] ?? 1 );
		$per_page = (int) ( $params['per_page'] ?? 10 );

		return $this->where( $where, $per_page, $page, $order_by, $order );
	}
}

