<?php

namespace Limb_Chatbot\Includes\Services\Jobs;

use Limb_Chatbot\Includes\Data_Objects\Job;
use Limb_Chatbot\Includes\Data_Objects\Task;
use Limb_Chatbot\Includes\Database_Strategies\WPDB;
use Limb_Chatbot\Includes\Repositories\Job_Task_Repository;

/**
 * Job Statistics Service
 *
 * Handles all statistics-related operations for jobs including:
 * - Task counts and status tracking
 * - Progress calculation
 * - Stats updates and formatting
 *
 * Follows Single Responsibility Principle - all stats logic is centralized here.
 * Provides unified stats format for both standard and multitask handlers.
 *
 * @since 1.1.0
 */
class Job_Stats_Service {

	/**
	 * Constructor.
	 *
	 * @since 1.1.0
	 */
	public function __construct() {
	}

	/**
	 * Get initial stats structure for a new job.
	 *
	 * @param  int  $total_tasks  Total number of tasks expected.
	 * @param  bool  $is_multitask  Whether this is a multitask handler.
	 *
	 * @return array Initial stats array.
	 * @since 1.1.0
	 */
	public function get_initial_stats( int $total_tasks, bool $is_multitask = false ): array {
		$stats = [
			'total_tasks'      => $total_tasks,
			'tasks_generated'  => 0,
			'pending_tasks'    => 0,
			'processing_tasks' => 0,
			'completed_tasks'  => 0,
			'failed_tasks'     => 0,
			'progress_percent' => 0.0,
		];

		// Add parent task tracking for multitask handlers
		if ( $is_multitask ) {
			$stats['parent_tasks_total']     = $total_tasks;
			$stats['parent_tasks_completed']  = 0;
			$stats['parent_tasks_failed']     = 0;
		}

		return $stats;
	}

	/**
	 * Calculate unified stats for standard (non-multitask) handlers.
	 *
	 * @param  Job  $job  Job instance.
	 * @param  Job_Task_Repository  $task_repository  Task repository.
	 *
	 * @return array Unified stats array.
	 * @since 1.1.0
	 */
	public function calculate_standard_stats( Job $job, Job_Task_Repository $task_repository ): array {
		$task_counts      = $task_repository->get_task_counts_by_status( $job->get_id() );
		$progress_percent = $this->calculate_standard_progress( $task_counts );

		return [
			'total_tasks'      => $task_counts['total'] ?? 0,
			'tasks_generated'  => $task_counts['total'] ?? 0,
			'pending_tasks'    => $task_counts['pending'] ?? 0,
			'processing_tasks' => $task_counts['processing'] ?? 0,
			'completed_tasks'  => $task_counts['completed'] ?? 0,
			'failed_tasks'     => $task_counts['failed'] ?? 0,
			'progress_percent' => $progress_percent,
		];
	}

	/**
	 * Calculate unified stats for multitask handlers.
	 *
	 * @param  Job  $job  Job instance.
	 * @param  Job_Task_Repository  $task_repository  Task repository.
	 *
	 * @return array Unified stats array.
	 * @since 1.1.0
	 */
	public function calculate_multitask_stats( Job $job, Job_Task_Repository $task_repository ): array {
		// Get child task counts (these are the actual tasks being processed)
		$child_task_counts = $task_repository->get_child_task_counts_by_status( $job->get_id() );
		$progress_percent = $this->calculate_multitask_progress( $job->get_id(), $task_repository );

		// Get parent task counts
		$parent_task_counts = $this->get_parent_task_counts( $job->get_id(), $task_repository );

		// Determine total tasks (use max of stored and current to handle dynamic generation)
		$stored_stats     = $job->get_stats() ?? [];
		$stored_total     = $stored_stats['total_tasks'] ?? 0;
		$current_total    = $child_task_counts['total'] ?? 0;
		$total_tasks      = max( $stored_total, $current_total );

		// Calculate completed_parents_count (same as parent_tasks_completed for consistency)
		$completed_parents_count = $parent_task_counts['completed'] ?? 0;

		return [
			'total_tasks'             => $total_tasks,
			'tasks_generated'         => $total_tasks,
			'pending_tasks'           => $child_task_counts['pending'] ?? 0,
			'processing_tasks'        => $child_task_counts['processing'] ?? 0,
			'completed_tasks'         => $child_task_counts['completed'] ?? 0,
			'failed_tasks'            => $child_task_counts['failed'] ?? 0,
			'progress_percent'        => $progress_percent,
			'parent_tasks_total'      => $parent_task_counts['total'] ?? 0,
			'parent_tasks_completed'  => $completed_parents_count,
			'parent_tasks_failed'     => $parent_task_counts['failed'] ?? 0,
			'completed_parents_count' => $completed_parents_count,
		];
	}

	/**
	 * Calculate progress percentage for standard (non-multitask) handlers.
	 *
	 * Formula: (processed / total) * 100
	 * Counts both completed and failed tasks as "processed" for progress.
	 *
	 * @param  array  $task_counts  Task counts array.
	 *
	 * @return float Progress percentage (0-100).
	 * @since 1.1.0
	 */
	public function calculate_standard_progress( array $task_counts ): float {
		$total      = $task_counts['total'] ?? 0;
		$completed  = $task_counts['completed'] ?? 0;
		$failed     = $task_counts['failed'] ?? 0;
		$processing = $task_counts['processing'] ?? 0;

		// If no tasks exist, return 0
		if ( $total === 0 ) {
			return 0.0;
		}

		// Each task contributes 100% / total
		$per_task_share = 100.0 / $total;

		// Tasks that have started (processing, completed, or failed) contribute 50% of their share
		$started          = $processing + $completed + $failed;
		$started_progress = $started * ( $per_task_share * 0.5 );

		// Tasks that are completed or failed contribute the remaining 50% of their share
		$finished          = $completed + $failed;
		$finished_progress = $finished * ( $per_task_share * 0.5 );

		$percent = $started_progress + $finished_progress;

		// Ensure percentage is between 0 and 100
		return round( min( 100.0, max( 0.0, $percent ) ), 2 );
	}

	/**
	 * Calculate progress percentage for multitask handlers.
	 *
	 * Progress is based on parent task completion, where each parent contributes equally.
	 *
	 * @param  int  $job_id  Job ID.
	 * @param  Job_Task_Repository  $task_repository  Task repository.
	 *
	 * @return float Progress percentage (0-100).
	 * @since 1.1.0
	 */
	public function calculate_multitask_progress( int $job_id, Job_Task_Repository $task_repository ): float {
		// Get completion percentages for each parent task
		$parent_completions = $task_repository->get_parent_task_completion_percentages( $job_id );
		if ( empty( $parent_completions ) ) {
			return 0.0;
		}

		// Get ALL parent tasks (no limit) to ensure we get all of them
		$parents = $task_repository->where( [ 'job_id' => $job_id, 'parent_task_id' => null ], -1, -1, 'id', 'ASC' );
		
		// Get actual total count from database using direct SQL to ensure accuracy
		$wpdb       = WPDB::instance()->get_wpdb();
		$table_name = $wpdb->prefix . Task::TABLE_NAME;
		$total_parents = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$table_name} WHERE job_id = %d AND parent_task_id IS NULL",
				$job_id
			)
		);
		
		if ( $total_parents === 0 ) {
			return 0.0;
		}

		// Get all parent task IDs from the collection
		$parent_task_ids  = $parents->pluck( 'id' );
		$total_completion = 0.0;
		
		// Calculate completion for all parents found in the collection
		// If a parent doesn't have a completion percentage, it defaults to 0% (hasn't started)
		foreach ( $parent_task_ids as $parent_task_id ) {
			$parent_task_id = (int) $parent_task_id;
			// Get completion for this parent task (default to 0% if not found)
			$completion       = $parent_completions[ $parent_task_id ] ?? 0.0;
			$total_completion += $completion;
		}

		// Ensure we account for ALL parents in the database
		// If we found fewer parent IDs than the total count, the missing ones contribute 0%
		$found_parents_count = count( $parent_task_ids );
		if ( $found_parents_count < $total_parents ) {
			// There are more parents in the database than we found in the collection
			// This shouldn't happen if we used -1 limit, but handle it just in case
			// Missing parents contribute 0% (they haven't started yet)
			// The total_completion already accounts for found parents, missing ones are 0%
		}

		// Calculate average completion percentage across all parents
		// Each parent contributes equally (100% / total_parents)
		$progress = $total_completion / $total_parents;

		return round( min( 100.0, max( 0.0, $progress ) ), 2 );
	}

	/**
	 * Get parent task counts for a job.
	 *
	 * For multitask handlers, a parent is considered "completed" when all its child tasks
	 * are done (completed or failed), not when the parent task status is COMPLETED.
	 *
	 * @param  int  $job_id  Job ID.
	 * @param  Job_Task_Repository  $task_repository  Task repository.
	 *
	 * @return array Parent task counts.
	 * @since 1.1.0
	 */
	private function get_parent_task_counts( int $job_id, Job_Task_Repository $task_repository ): array {
		$wpdb       = WPDB::instance()->get_wpdb();
		$table_name = $wpdb->prefix . Task::TABLE_NAME;

		// Get actual total count from database using direct SQL to ensure accuracy
		$total = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$table_name} WHERE job_id = %d AND parent_task_id IS NULL",
				$job_id
			)
		);

		// Get ALL parent tasks (no limit) to ensure we check all of them
		$parent_tasks = $task_repository->where( [ 'job_id' => $job_id, 'parent_task_id' => null ], -1, -1, 'id', 'ASC' );
		
		$completed = 0;
		$failed    = 0;

		// Check each parent to see if all its children are done
		foreach ( $parent_tasks->get() as $parent_task ) {
			$parent_task_id = (int) $parent_task->get_id();
			
			// Get child task counts for this parent
			$results = $wpdb->get_row(
				$wpdb->prepare(
					"SELECT 
						COUNT(*) as total,
						SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) as pending,
						SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) as processing,
						SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) as completed,
						SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) as failed
					FROM {$table_name}
					WHERE job_id = %d AND parent_task_id = %d",
					Task::STATUS_PENDING,
					Task::STATUS_PROCESSING,
					Task::STATUS_COMPLETED,
					Task::STATUS_FAILED,
					$job_id,
					$parent_task_id
				),
				ARRAY_A
			);

			$total_children = $results ? (int) ( $results['total'] ?? 0 ) : 0;
			$pending        = $results ? (int) ( $results['pending'] ?? 0 ) : 0;
			$processing     = $results ? (int) ( $results['processing'] ?? 0 ) : 0;

			// Parent is completed when all children are done (no pending or processing)
			if ( $total_children > 0 && $pending === 0 && $processing === 0 ) {
				// Check if parent task itself is failed
				if ( $parent_task->get_status() === Task::STATUS_FAILED ) {
					$failed ++;
				} else {
					$completed ++;
				}
			} elseif ( $total_children === 0 ) {
				// No children - check parent task status directly
				if ( $parent_task->get_status() === Task::STATUS_FAILED ) {
					$failed ++;
				} elseif ( $parent_task->get_status() === Task::STATUS_COMPLETED ) {
					$completed ++;
				}
			}
		}

		return [
			'total'     => $total,
			'completed' => $completed,
			'failed'    => $failed,
		];
	}
}
