<?php defined('BASEPATH') or exit('No direct script access allowed');

class Auto_update_worker extends CI_Controller
{
	private function jobDir(): string
	{
		$dir = rtrim(get_temp_dir(), '/\\') . '/erp_update_jobs/';
		if (!is_dir($dir)) {
			@mkdir($dir, 0777, true);
		}
		return $dir;
	}

	private function jobFile(string $token): string
	{
		return $this->jobDir() . preg_replace('/[^a-zA-Z0-9_-]/', '', $token) . '.json';
	}

	private function lockFile(): string
	{
		return $this->jobDir() . 'update.lock';
	}

	private function loadJob(string $token): array
	{
		$file = $this->jobFile($token);
		if (!is_file($file)) {
			return array();
		}
		$data = json_decode((string) file_get_contents($file), true);
		return is_array($data) ? $data : array();
	}

	private function saveJob(string $token, array $data): void
	{
		$data['token'] = $token;
		$data['updated_at'] = time();
		file_put_contents($this->jobFile($token), json_encode($data), LOCK_EX);
	}

	private function finishJob(string $token): void
	{
		$lockFile = $this->lockFile();
		if (is_file($lockFile)) {
			$current = trim((string) file_get_contents($lockFile));
			if ($current === $token) {
				@unlink($lockFile);
			}
		}
	}

	private function upgradeAppConfig(): array
	{
		$this->load->helper('app_config');
		return app_config_upgrade();
	}

	public function run($token = '')
	{
		$token = preg_replace('/[^a-zA-Z0-9_-]/', '', (string) $token);
		if ($token === '') {
			return;
		}

		@set_time_limit(0);
		@ignore_user_abort(true);
		@ini_set('memory_limit', '-1');

		$CI = $this;
		$CI->load->model('admin_model');
		$CI->load->config('migration');
		$job = $this->loadJob($token);
		if (empty($job)) {
			$this->finishJob($token);
			return;
		}

		if (php_sapi_name() !== 'cli') {
			$this->output->set_content_type('application/json')->set_output(json_encode(array(
				'success' => true,
				'started' => true,
				'token' => $token
			)));
			if (function_exists('fastcgi_finish_request')) {
				@fastcgi_finish_request();
			}
		}

		try {
			$currentVersion = (int) ($job['current_version'] ?? $CI->admin_model->get_current_db_version());
			$targetVersion = (int) ($job['latest_version'] ?? config_item('migration_version'));
			$totalSteps = max(1, $targetVersion - $currentVersion);

			$this->saveJob($token, array_merge($job, array(
				'status' => 'running',
				'message' => 'Starting database migration',
				'progress' => 1,
				'current_version' => $currentVersion,
				'latest_version' => $targetVersion,
				'done' => false,
				'error' => ''
			)));

			$CI->load->library('migration', array(
				'migration_enabled' => true,
				'migration_type' => $CI->config->item('migration_type'),
				'migration_table' => $CI->config->item('migration_table'),
				'migration_auto_latest' => $CI->config->item('migration_auto_latest'),
				'migration_version' => $CI->config->item('migration_version'),
				'migration_path' => $CI->config->item('migration_path')
			));

			for ($next = $currentVersion + 1; $next <= $targetVersion; $next++) {
				$stepLabel = 'Applying migration ' . $next . ' of ' . $targetVersion;
				$progress = (int) floor((($next - 1 - $currentVersion) / $totalSteps) * 100);
				if ($progress < 1) {
					$progress = 1;
				}
				$this->saveJob($token, array_merge($job, array(
					'status' => 'running',
					'message' => $stepLabel,
					'progress' => $progress,
					'current_version' => $next - 1,
					'latest_version' => $targetVersion,
					'done' => false,
					'error' => ''
				)));

				if ($CI->migration->version($next) === FALSE) {
					$error = (string) $CI->migration->error_string();
					$this->saveJob($token, array_merge($job, array(
						'status' => 'failed',
						'message' => $error !== '' ? $error : 'Migration failed.',
						'progress' => $progress,
						'current_version' => (int) $CI->admin_model->get_current_db_version(),
						'latest_version' => $targetVersion,
						'done' => false,
						'error' => $error
					)));
					$this->finishJob($token);
					return;
				}

				$currentNow = (int) $CI->admin_model->get_current_db_version();
				$progress = (int) floor((($currentNow - $currentVersion) / $totalSteps) * 100);
				if ($currentNow >= $targetVersion) {
					$progress = 100;
				}
				$this->saveJob($token, array_merge($job, array(
					'status' => 'running',
					'message' => 'Migration ' . $currentNow . ' completed',
					'progress' => $progress,
					'current_version' => $currentNow,
					'latest_version' => $targetVersion,
					'done' => false,
					'error' => ''
				)));
			}

			$humanVersion = '';
			$migrationVersion = (string) config_item('migration_version');
			if ($migrationVersion !== '') {
				$humanVersion = implode('.', str_split($migrationVersion, 1));
			}
			if ($humanVersion !== '') {
				$CI->db->where('config_key', 'version')->update('tbl_config', array('value' => $humanVersion));
			}

			$appConfig = $this->upgradeAppConfig();
			if (empty($appConfig['success'])) {
				$this->saveJob($token, array_merge($job, array(
					'status' => 'failed',
					'message' => $appConfig['message'],
					'progress' => 100,
					'current_version' => (int) $CI->admin_model->get_current_db_version(),
					'latest_version' => $targetVersion,
					'done' => false,
					'error' => $appConfig['message']
				)));
				$this->finishJob($token);
				return;
			}

			$this->saveJob($token, array_merge($job, array(
				'status' => 'done',
				'message' => 'Database upgrade completed successfully.',
				'progress' => 100,
				'current_version' => (int) $CI->admin_model->get_current_db_version(),
				'latest_version' => $targetVersion,
				'done' => true,
				'error' => ''
			)));

			$this->finishJob($token);
		} catch (Throwable $e) {
			$this->saveJob($token, array_merge($job, array(
				'status' => 'failed',
				'message' => 'Database upgrade failed.',
				'progress' => (int) ($job['progress'] ?? 0),
				'current_version' => (int) ($job['current_version'] ?? 0),
				'latest_version' => (int) ($job['latest_version'] ?? config_item('migration_version')),
				'done' => false,
				'error' => $e->getMessage()
			)));
			$this->finishJob($token);
		}
	}
}
