import { BillProvider } from '#providers/BillProvider';
import { Bill } from '#models/Bill';
import { Op } from 'sequelize';
import LoggingService from '#services/file-system/logService';

const CLASS_NAME = 'BillOverdueJob';

/**
 * Update pending bills to overdue status when due_date has passed
 * Uses batch processing with cursor-based pagination to handle large datasets
 */
export class BillOverdueJob {
	private logger: LoggingService;
	private billProvider: BillProvider;
	private readonly BATCH_SIZE = 100; // Process 100 records per batch

	constructor() {
		this.logger = new LoggingService();
		this.billProvider = BillProvider.getInstance();
	}

	/**
	 * Main method to update overdue bills
	 * Uses cursor-based pagination to process records in batches
	 */
	async updateOverdueBills(): Promise<number> {
		const METHOD_NAME = 'updateOverdueBills';
		const SOURCE = `${CLASS_NAME}.${METHOD_NAME}`;

		try {
			const today = new Date();
			today.setHours(0, 0, 0, 0); // Set to start of day

			let totalUpdated = 0;
			let lastId: string | null = null;
			let hasMore = true;

			// Batch processing with cursor-based pagination
			while (hasMore) {
				// Get bills that are pending and past due date
				const bills: any[] = await Bill.findAll({
					where: {
						status: 'pending',
						due_date: {
							[Op.lt]: today,
						},
						deleted_at: null,
						...(lastId ? { id: { [Op.gt]: lastId } } : {}),
					},
					attributes: ['id', 'code', 'due_date'],
					limit: this.BATCH_SIZE,
					order: [['id', 'ASC']],
					raw: true,
				});

				if (bills.length === 0) {
					hasMore = false;
					break;
				}

				// Extract bill IDs for batch update
				const billIds = bills.map((bill) => bill.id);
				lastId = bills[bills.length - 1].id;

				// Batch update status to overdue
				const [updatedCount] = await Bill.update(
					{
						status: 'overdue',
						updated_at: new Date(),
					},
					{
						where: {
							id: {
								[Op.in]: billIds,
							},
						},
					},
				);

				totalUpdated += updatedCount;

				this.logger.logAsync(
					'SCHEDULE',
					SOURCE,
					`Updated batch of ${updatedCount} bills to overdue. Last ID: ${lastId}`,
					null,
				);

				// Small delay between batches to reduce database load
				await this.sleep(100);
			}

			if (totalUpdated > 0) {
				this.logger.logAsync(
					'SCHEDULE',
					SOURCE,
					`Successfully updated ${totalUpdated} bills to overdue status`,
					null,
				);
			} else {
				this.logger.logAsync(
					'SCHEDULE',
					SOURCE,
					'No overdue bills to update',
					null,
				);
			}

			return totalUpdated;
		} catch (error) {
			console.error(`[SCHEDULE] Error in BillOverdueJob:`, error);
			this.logger.logAsync(
				'SCHEDULE',
				SOURCE,
				`Error updating overdue bills: ${error}`,
				null,
			);
			throw error;
		}
	}

	/**
	 * Helper method to add delay between batches
	 */
	private sleep(ms: number): Promise<void> {
		return new Promise((resolve) => setTimeout(resolve, ms));
	}
}

export default BillOverdueJob;
