import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Config, ConfigDocument } from '../app-config/config/config.model';
import { migrations } from './registry/migrations';
import { CURRENT_MIGRATION_VERSION } from './migration.version';

@Injectable()
export class MigrationService implements OnModuleInit {
	private readonly logger = new Logger(MigrationService.name);
	private readonly CURRENT_VERSION = CURRENT_MIGRATION_VERSION;
	private readonly VERSION_KEY = 'db_version';

	constructor(
		@InjectModel(Config.name) private configModel: Model<ConfigDocument>
	) {
		console.log('MigrationService: constructor - initializing MigrationService');
	}

	async onModuleInit() {
		console.log(`MigrationService: onModuleInit - starting migration check, current code version: ${this.CURRENT_VERSION}`);
		await this.runMigrations();
	}

	private async getDbVersion(): Promise<number | null> {
		console.log('MigrationService: getDbVersion - retrieving database version from config');
		const config = await this.configModel.findOne({ key: this.VERSION_KEY });
		if (!config) {
			console.log('MigrationService: getDbVersion - no version found in database, returning null');
			return null;
		}
		const version = parseInt(config.value, 10);
		console.log(`MigrationService: getDbVersion - found database version: ${version}`);
		return isNaN(version) ? null : version;
	}

	private async setDbVersion(version: number): Promise<void> {
		console.log(`MigrationService: setDbVersion - setting database version to: ${version}`);
		await this.configModel.findOneAndUpdate(
			{ key: this.VERSION_KEY },
			{ 
				key: this.VERSION_KEY, 
				value: version.toString(),
				description: 'Database schema version'
			},
			{ upsert: true, new: true }
		);
		console.log(`MigrationService: setDbVersion - database version updated successfully`);
	}

	private async runMigrations(): Promise<void> {
		try {
			const dbVersion = await this.getDbVersion();
			const currentVersion = this.CURRENT_VERSION;
			const effectiveDbVersion = dbVersion ?? 0;

			console.log(`MigrationService: runMigrations - comparing versions - DB: ${effectiveDbVersion} (${dbVersion === null ? 'none, treating as 0' : 'found'}), Code: ${currentVersion}`);

			if (effectiveDbVersion === currentVersion) {
				console.log('MigrationService: runMigrations - versions match, no migrations needed');
				return;
			}

			const migrationsToRun = this.getMigrationsToRun(effectiveDbVersion, currentVersion);
			
			if (migrationsToRun.length === 0) {
				console.log('MigrationService: runMigrations - no migrations to run');
				await this.setDbVersion(currentVersion);
				return;
			}

			console.log(`MigrationService: runMigrations - found ${migrationsToRun.length} migrations to run`);

			for (const migration of migrationsToRun) {
				console.log(`MigrationService: runMigrations - running migration: ${migration.version} - ${migration.name}`);
				try {
					await migration.up(this.configModel);
					console.log(`MigrationService: runMigrations - migration ${migration.version} completed successfully`);
					await this.setDbVersion(migration.version);
				} catch (error) {
					this.logger.error(`MigrationService: runMigrations - migration ${migration.version} failed: ${error.message}`, error.stack);
					throw error;
				}
			}

			console.log(`MigrationService: runMigrations - all migrations completed, setting final version to: ${currentVersion}`);
			await this.setDbVersion(currentVersion);
		} catch (error) {
			this.logger.error(`MigrationService: runMigrations - migration process failed: ${error.message}`, error.stack);
			throw error;
		}
	}

	private getMigrationsToRun(dbVersion: number, targetVersion: number): Array<{ version: number; name: string; up: (configModel: Model<ConfigDocument>) => Promise<void> }> {
		const sortedMigrations = migrations.sort((a, b) => {
			return a.version - b.version;
		});

		const migrationsToRun: Array<{ version: number; name: string; up: (configModel: Model<ConfigDocument>) => Promise<void> }> = [];

		for (const migration of sortedMigrations) {
			if (migration.version > dbVersion && migration.version <= targetVersion) {
				migrationsToRun.push(migration);
			}
		}

		return migrationsToRun;
	}
}

