<?php

namespace JetBackup\Queue;

use JetBackup\Exception\QueueException;
use JetBackup\JetBackup;
use SleekDB\Exceptions\InvalidArgumentException;
use SleekDB\Exceptions\IOException;

if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');

class Queue {

	public const QUEUE_TYPE_BACKUP = 1;
	public const QUEUE_TYPE_RESTORE = 2;
	public const QUEUE_TYPE_DOWNLOAD = 4;
    public const QUEUE_TYPE_DOWNLOAD_BACKUP_LOG = 5;
    public const QUEUE_TYPE_REINDEX = 8;
	public const QUEUE_TYPE_RETENTION_CLEANUP = 16;
	public const QUEUE_TYPE_SYSTEM = 32;
	public const QUEUE_TYPE_EXPORT = 64;
    public const QUEUE_TYPE_EXTRACT = 128;

	public const QUEUE_TYPES = [
		self::QUEUE_TYPE_BACKUP,
		self::QUEUE_TYPE_RESTORE,
		self::QUEUE_TYPE_DOWNLOAD,
        self::QUEUE_TYPE_DOWNLOAD_BACKUP_LOG,
		self::QUEUE_TYPE_REINDEX,
		self::QUEUE_TYPE_RETENTION_CLEANUP,
		self::QUEUE_TYPE_SYSTEM,
		self::QUEUE_TYPE_EXPORT,
		self::QUEUE_TYPE_EXTRACT,
	];

	public const QUEUE_STATUS_NAMES = [
		self::QUEUE_TYPE_BACKUP => self::STATUS_BACKUP_ACCOUNT_NAMES,
		self::QUEUE_TYPE_RESTORE => self::STATUS_PRE_RESTORE_NAMES,
		self::QUEUE_TYPE_DOWNLOAD => self::STATUS_DOWNLOAD_NAMES,
        self::QUEUE_TYPE_DOWNLOAD_BACKUP_LOG => self::STATUS_DOWNLOAD_LOG_NAMES,
		self::QUEUE_TYPE_REINDEX => self::STATUS_REINDEX_NAMES,
		self::QUEUE_TYPE_RETENTION_CLEANUP => self::STATUS_CLEANUP_NAMES,
		self::QUEUE_TYPE_SYSTEM => self::STATUS_SYSTEM_NAMES,
		self::QUEUE_TYPE_EXPORT => self::STATUS_EXPORT_NAMES,
		self::QUEUE_TYPE_EXTRACT => self::STATUS_EXTRACT_NAMES,
	];

	public const QUEUE_TYPES_NAMES = [
		self::QUEUE_TYPE_BACKUP                 => 'Backup',
		self::QUEUE_TYPE_RESTORE                => 'Restore',
		self::QUEUE_TYPE_DOWNLOAD               => 'Download',
        self::QUEUE_TYPE_DOWNLOAD_BACKUP_LOG    => 'Download Backup Log',
		self::QUEUE_TYPE_REINDEX                => 'Reindex',
		self::QUEUE_TYPE_RETENTION_CLEANUP      => 'Retention Cleanup',
		self::QUEUE_TYPE_SYSTEM                 => 'System',
		self::QUEUE_TYPE_EXPORT                 => 'Export',
		self::QUEUE_TYPE_EXTRACT                => 'Extract',
	];

	// Backup Account Statuses
	public const STATUS_BACKUP_ACCOUNT_DUMPING_DB = 10;
	public const STATUS_BACKUP_ACCOUNT_ARCHIVING = 20;
	public const STATUS_BACKUP_ACCOUNT_COMPRESSING = 21;
	public const STATUS_BACKUP_ACCOUNT_SEND_TO_DESTINATION = 30;
	public const STATUS_BACKUP_ACCOUNT_NAMES = [
		self::STATUS_BACKUP_ACCOUNT_DUMPING_DB              => 'Dumping Database',
		self::STATUS_BACKUP_ACCOUNT_ARCHIVING               => 'Archiving',
		self::STATUS_BACKUP_ACCOUNT_COMPRESSING             => 'Compressing',
		self::STATUS_BACKUP_ACCOUNT_SEND_TO_DESTINATION     => 'Transferring to Destination',
	];

	// Backup Config Statuses
	public const STATUS_BACKUP_CONFIG_ARCHIVING = 20;
	public const STATUS_BACKUP_CONFIG_COMPRESSING = 21;
	public const STATUS_BACKUP_CONFIG_SEND_TO_DESTINATION = 30;
	public const STATUS_BACKUP_CONFIG_NAMES = [
		self::STATUS_BACKUP_CONFIG_ARCHIVING               => 'Archiving',
		self::STATUS_BACKUP_CONFIG_COMPRESSING             => 'Compressing',
		self::STATUS_BACKUP_CONFIG_SEND_TO_DESTINATION     => 'Transferring to Destination',
	];

	// Restore Statuses
	public const STATUS_RESTORE_DOWNLOAD = 10;
	public const STATUS_RESTORE_EXTRACT = 20;
	public const STATUS_RESTORE_BUILD_URL = 30;
	public const STATUS_RESTORE_WAITING_FOR_RESTORE = 40;
	public const STATUS_RESTORE_DATABASE = 50;
	public const STATUS_RESTORE_FILES = 55;
	public const STATUS_RESTORE_POST_RESTORE_DB_PREFIX = 60;
	public const STATUS_RESTORE_POST_RESTORE_DOMAIN_MIGRATION = 65;
	public const STATUS_RESTORE_POST_RESTORE_PLUGIN_ACTIONS = 70;
	public const STATUS_RESTORE_POST_RESTORE_HEALTH_CHECK = 75;
	
	// Restore JB Linux Statuses
	public const STATUS_RESTORE_JB_IMPORTING_DB = 55;
	public const STATUS_RESTORE_JB_DATABASES = 60;
	public const STATUS_RESTORE_JB_DATABASE_USERS = 65;
	public const STATUS_RESTORE_JB_HOMEDIR = 70;
	public const STATUS_RESTORE_JB_POST = 75;
	public const STATUS_RESTORE_JB_PRE = 80;

	public const STATUS_PRE_RESTORE_NAMES = [
		self::STATUS_RESTORE_DOWNLOAD                       => 'Downloading',
		self::STATUS_RESTORE_EXTRACT                        => 'Extracting',
		self::STATUS_RESTORE_BUILD_URL                      => 'Building Restore URL',
		self::STATUS_RESTORE_WAITING_FOR_RESTORE            => 'Waiting for external restore',
	];

	public const STATUS_RESTORE_NAMES = [
		self::STATUS_RESTORE_DATABASE                           => 'Restoring Database',
		self::STATUS_RESTORE_FILES                              => 'Restoring Files',
		self::STATUS_RESTORE_POST_RESTORE_DB_PREFIX             => 'Post Restore - DB Prefix',
		self::STATUS_RESTORE_POST_RESTORE_DOMAIN_MIGRATION      => 'Post Restore - Domain Migration',
		self::STATUS_RESTORE_POST_RESTORE_PLUGIN_ACTIONS        => 'Post Restore - Plugin Actions',
		self::STATUS_RESTORE_POST_RESTORE_HEALTH_CHECK          => 'Post Restore - Health Check',
	];

	// Reindex Statuses
	public const STATUS_REINDEX_MARKING_SNAPSHOTS = 10;
	public const STATUS_REINDEX_INDEXING_SNAPSHOTS = 30;
	public const STATUS_REINDEX_DELETE_SNAPSHOTS = 40;
	public const STATUS_REINDEX_NAMES = [
		self::STATUS_REINDEX_MARKING_SNAPSHOTS              => 'Marking Snapshots for delete',
		self::STATUS_REINDEX_INDEXING_SNAPSHOTS             => 'Indexing Snapshots',
		self::STATUS_REINDEX_DELETE_SNAPSHOTS               => 'Deleting Snapshots',
	];
	
	// Extract Statuses
	public const STATUS_EXTRACT_DOWNLOAD = 10;
	public const STATUS_EXTRACT_EXTRACT = 15;
	public const STATUS_EXTRACT_LEGACY = 25;
	public const STATUS_EXTRACT_NAMES = [
		self::STATUS_EXTRACT_DOWNLOAD                       => 'Downloading',
		self::STATUS_EXTRACT_EXTRACT                        => 'Extracting',
		self::STATUS_EXTRACT_LEGACY                         => 'Extracting Legacy',
	];

	// Export Statuses
	public const STATUS_EXPORT_DOWNLOAD = 10;
	public const STATUS_EXPORT_EXTRACT = 15;
	public const STATUS_EXPORT_BUILD = 25;
	public const STATUS_EXPORT_NAMES = [
		self::STATUS_EXPORT_DOWNLOAD                        => 'Downloading',
		self::STATUS_EXPORT_EXTRACT                         => 'Extracting',
		self::STATUS_EXPORT_BUILD                           => 'Building',
	];

	// Download Statuses
	public const STATUS_DOWNLOAD_DOWNLOAD = 10;
	public const STATUS_DOWNLOAD_ARCHIVE = 15;
	public const STATUS_DOWNLOAD_COMPRESS = 20;
	public const STATUS_DOWNLOAD_NAMES = [
		self::STATUS_DOWNLOAD_DOWNLOAD                       => 'Downloading',
		self::STATUS_DOWNLOAD_ARCHIVE                        => 'Archiving',
		self::STATUS_DOWNLOAD_COMPRESS                       => 'Compressing',

	];

    public const STATUS_DOWNLOAD_LOG_DOWNLOADING = 10;
    public const STATUS_DOWNLOAD_LOG_NAMES = [
        self::STATUS_DOWNLOAD_LOG_DOWNLOADING                       => 'Downloading',
    ];

	// Retention Cleanup Statuses
	public const STATUS_CLEANUP_DELETING = 10;
	public const STATUS_CLEANUP_NAMES = [
		self::STATUS_CLEANUP_DELETING                        => 'Deleting',
	];
	
	// System Statuses
	public const STATUS_SYSTEM_JOB_MONITOR = 10;
	public const STATUS_SYSTEM_DB_CLEANUP = 15;
	public const STATUS_SYSTEM_UPLOAD_CLEANUP = 17;
	public const STATUS_SYSTEM_SYSTEM_CLEANUP = 20;
	public const STATUS_SYSTEM_LOGS_CLEANUP = 25;
	public const STATUS_SYSTEM_VALIDATE_CHECKSUMS = 30;

	public const STATUS_SYSTEM_DAILY_ALERTS = 35;
	public const STATUS_SYSTEM_NAMES = [
		self::STATUS_SYSTEM_JOB_MONITOR                     => 'Backup Job Monitoring',
		self::STATUS_SYSTEM_DB_CLEANUP                      => 'Database Cleanup',
		self::STATUS_SYSTEM_UPLOAD_CLEANUP                  => 'Uploads Cleanup',
		self::STATUS_SYSTEM_SYSTEM_CLEANUP                  => 'System Cleanup',
		self::STATUS_SYSTEM_LOGS_CLEANUP                    => 'Logs Cleanup',
		self::STATUS_SYSTEM_VALIDATE_CHECKSUMS              => 'Validating Checksums',
		self::STATUS_SYSTEM_DAILY_ALERTS              => 'Process Daily Alerts',
	];

	public const STATUS_PENDING = 1;
	public const STATUS_STARTED = 2;
	public const STATUS_PREPARING = 3;
	public const STATUS_DONE = 100;
	public const STATUS_PARTIALLY = 101;
	public const STATUS_FAILED = 102;
	public const STATUS_ABORTED = 103;
	public const STATUS_NEVER_FINISHED = 104;

	public const REQUIRES_CLEANUP = [
		self::STATUS_FAILED,
		self::STATUS_ABORTED,
		self::STATUS_NEVER_FINISHED,
	];

	public const STATUS_NAMES = [
		self::STATUS_PENDING            => 'Pending',
		self::STATUS_STARTED            => 'Started',
		self::STATUS_PREPARING          => 'Preparing',
		self::STATUS_DONE               => 'Completed',
		self::STATUS_PARTIALLY          => 'Partially Completed',
		self::STATUS_FAILED             => 'Failed',
		self::STATUS_ABORTED            => 'Aborted',
		self::STATUS_NEVER_FINISHED     => 'Never Finished',
	];


	public function __construct() {}


	public function getNext():?QueueItem {
		$res = QueueItem::query()
            ->select([JetBackup::ID_FIELD])
            ->where([QueueItem::STATUS, '<', self::STATUS_DONE])
            ->orderBy([QueueItem::STATUS => 'asc'])
            ->limit(1)
            ->getQuery()
            ->first();
		
		return $res ? new QueueItem($res[JetBackup::ID_FIELD]) : null;
	}	
	
	public static function next():?QueueItem {
		return (new Queue())->getNext();
	}

	/**
	 * @throws IOException
	 * @throws InvalidArgumentException
	 */
	public static function inQueue($type, $_id=null):bool {
		$query = QueueItem::query()
			->where([ QueueItem::TYPE, '=', $type ])
			->where([ QueueItem::STATUS, '<', self::STATUS_DONE ]);
		if ($_id !== null) $query->where([ QueueItem::ITEM_ID, '=', $_id ]);
		return !!$query->getQuery()->first();
	}

	/**
	 * @throws InvalidArgumentException
	 * @throws IOException
	 */
	public static function getTotalActiveItems(): int {

		return count(QueueItem::query()
             ->select([JetBackup::ID_FIELD])
             ->where([QueueItem::STATUS, '<', self::STATUS_DONE])
             ->getQuery()
             ->fetch());
	}
	/**
	 * @throws InvalidArgumentException
	 * @throws IOException
	 */
	public static function getTotalPendingItems(): int {
		return count(QueueItem::query()
			->select([JetBackup::ID_FIELD])
			->where([QueueItem::STATUS, '=', self::STATUS_PENDING])
			->getQuery()
			->fetch());
	}

	/**
	 * @throws InvalidArgumentException
	 * @throws IOException
	 */
	public static function getTotalCompletedItems(): int {
		return count(QueueItem::query()
			->select([JetBackup::ID_FIELD])
			->where([QueueItem::STATUS, '=', self::STATUS_DONE])
			->getQuery()
			->fetch());
	}

	/**
	 * @throws InvalidArgumentException
	 * @throws IOException
	 */
	public static function getTotalAbortedItems(): int {
		return count(QueueItem::query()
			->select([JetBackup::ID_FIELD])
			->where([QueueItem::STATUS, '=', self::STATUS_ABORTED])
			->getQuery()
			->fetch());
	}

	public static function clearCompleted(): void {

	QueueItem::query()->select([JetBackup::ID_FIELD])
		                      ->where([QueueItem::STATUS, '>=', self::STATUS_DONE])
		                      ->getQuery()
		                      ->delete();

	}



	/**
	 * @param QueueItem $item
	 *
	 * @return void
	 * @throws QueueException
	 * @throws IOException
	 * @throws InvalidArgumentException
	 */
	public static function addToQueue(QueueItem $item):void {
		if(self::inQueue($item->getType(), $item->getItemId())) throw new QueueException("Already in queue");
		$item->save();
	}
}