import { Models } from '../models/factory';
import { connectDb } from '../db';
import { TaskId } from '../services/database/types';
import TaskService, { Task, taskIdToLabel } from '../services/TaskService';
import { Services } from '../services/types';
import { logHeartbeat as logHeartbeatMessage } from './metrics';
import { Config, Env } from './types';
import { Day } from './time';

export default async function(env: Env, models: Models, config: Config, services: Services): Promise<TaskService> {
	// In production, use a separate DB connection pool for task state
	// management so that it is not affected by failed transactions in the
	// main connection pool. In dev/test, we reuse the main connection to
	// avoid exhausting Postgres connection slots in CI.
	const taskStateDb = env === Env.Prod ? await connectDb({ ...config.database, maxConnections: 1 }) : null;
	const taskService = new TaskService(env, models, config, services, taskStateDb);

	let tasks: Task[] = [
		{
			id: TaskId.DeleteExpiredTokens,
			description: taskIdToLabel(TaskId.DeleteExpiredTokens),
			schedule: '0 */6 * * *',
			run: (models: Models) => models.token().deleteExpiredTokens(),
		},

		{
			id: TaskId.UpdateTotalSizes,
			description: taskIdToLabel(TaskId.UpdateTotalSizes),
			schedule: '0 * * * *',
			run: (models: Models) => models.item().updateTotalSizes(),
		},

		{
			id: TaskId.CompressOldChanges,
			description: taskIdToLabel(TaskId.CompressOldChanges),
			schedule: '0 0 */2 * *',
			run: (models: Models) => models.change().compressOldChanges(),
		},

		{
			id: TaskId.ProcessUserDeletions,
			description: taskIdToLabel(TaskId.ProcessUserDeletions),
			schedule: '10 * * * *',
			run: (_models: Models, services: Services) => services.userDeletion.runMaintenance(),
		},

		// Need to do it relatively frequently so that if the user fixes
		// whatever was causing the oversized account, they can get it
		// re-enabled quickly. Also it's done on minute 30 because it depends on
		// the UpdateTotalSizes task being run.
		{
			id: TaskId.HandleOversizedAccounts,
			description: taskIdToLabel(TaskId.HandleOversizedAccounts),
			schedule: '30 */2 * * *',
			run: (models: Models) => models.user().handleOversizedAccounts(),
		},

		{
			id: TaskId.ProcessOrphanedItems,
			description: taskIdToLabel(TaskId.ProcessOrphanedItems),
			schedule: '15 * * * *',
			run: (models: Models) => models.item().processOrphanedItems(),
		},

		{
			id: TaskId.ProcessShares,
			description: taskIdToLabel(TaskId.ProcessShares),
			schedule: 'PT10S',
			run: (models: Models) => models.share().updateSharedItems3(),
		},

		{
			id: TaskId.ProcessEmails,
			description: taskIdToLabel(TaskId.ProcessEmails),
			schedule: '* * * * *',
			run: (_models: Models, services: Services) => services.email.runMaintenance(),
		},

		{
			id: TaskId.LogHeartbeatMessage,
			description: taskIdToLabel(TaskId.LogHeartbeatMessage),
			schedule: config.HEARTBEAT_MESSAGE_SCHEDULE,
			run: (_models: Models, _services: Services) => logHeartbeatMessage(),
		},

		{
			id: TaskId.DeleteExpiredAuthCodes,
			description: taskIdToLabel(TaskId.DeleteExpiredAuthCodes),
			schedule: '*/15 * * * *',
			run: (models: Models) => models.user().deleteExpiredAuthCodes(),
		},

		{
			id: TaskId.DeleteArchivedBackups,
			description: taskIdToLabel(TaskId.DeleteArchivedBackups),
			schedule: '0 0 * * *',
			run: (models: Models) => models.backupItem().deleteOldAccountBackups(),
		},
	];

	if (config.DELETE_EXPIRED_SESSIONS_SCHEDULE) {
		tasks.push({
			id: TaskId.DeleteExpiredSessions,
			description: taskIdToLabel(TaskId.DeleteExpiredSessions),
			schedule: config.DELETE_EXPIRED_SESSIONS_SCHEDULE,
			run: (models: Models) => models.session().deleteExpiredSessions(),
		});
	}

	if (config.USER_DATA_AUTO_DELETE_ENABLED) {
		tasks.push({
			id: TaskId.AutoAddDisabledAccountsForDeletion,
			description: taskIdToLabel(TaskId.AutoAddDisabledAccountsForDeletion),
			schedule: '0 14 * * *',
			run: (_models: Models, services: Services) => services.userDeletion.autoAddForDeletion(),
		});
	}

	if (config.EVENTS_AUTO_DELETE_ENABLED) {
		tasks.push({
			id: TaskId.DeleteOldEvents,
			description: taskIdToLabel(TaskId.DeleteOldEvents),
			schedule: '0 0 * * *',
			run: (models: Models) => models.event().deleteOldEvents(config.EVENTS_AUTO_DELETE_AFTER_DAYS * Day),
		});
	}

	if (config.isJoplinCloud) {
		tasks = tasks.concat([
			{
				id: TaskId.HandleBetaUserEmails,
				description: taskIdToLabel(TaskId.HandleBetaUserEmails),
				schedule: '0 12 * * *',
				run: (models: Models) => models.user().handleBetaUserEmails(),
			},
			{
				id: TaskId.HandleFailedPaymentSubscriptions,
				description: taskIdToLabel(TaskId.HandleFailedPaymentSubscriptions),
				schedule: '0 13 * * *',
				run: (models: Models) => models.user().handleFailedPaymentSubscriptions(),
			},
		]);
	}

	await taskService.registerTasks(tasks);

	await taskService.resetInterruptedTasks();

	return taskService;
}
