import { SessionStore } from '@backend/auth'
import { UserModifiableEntity } from '@backend/domain'
import { isArray } from 'es-toolkit/compat'
import { DeepPartial, FindOptionsWhere, Repository, SaveOptions, UpdateResult } from 'typeorm'
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'
import { BaseEntityRepository } from './base-entity.repository'

export abstract class UserModifiableEntityRepository<
	T extends UserModifiableEntity<T>,
> extends BaseEntityRepository<T> {
	constructor(override readonly _repository: Repository<T>) {
		super(_repository)
	}

	override save<K extends DeepPartial<T>>(entities: K[], options: SaveOptions & { reload: false }): Promise<T[]>
	override save<K extends DeepPartial<T>>(entities: K[], options?: SaveOptions | undefined): Promise<T[]>
	override save<K extends DeepPartial<T>>(entity: K, options: SaveOptions & { reload: false }): Promise<T>
	override save<K extends DeepPartial<T>>(entity: K, options?: SaveOptions | undefined): Promise<T>
	override save(entityOrEntities: DeepPartial<T> | DeepPartial<T>[], options?: SaveOptions): Promise<T[]> | Promise<T> {
		const userId = SessionStore.userId

		if (userId) {
			if (isArray(entityOrEntities)) {
				for (const entity of entityOrEntities) this.assignAuditFields(entity, userId)
			} else this.assignAuditFields(entityOrEntities, userId)
		}

		return super.save(entityOrEntities as never, options)
	}

	override update(criteria: FindOptionsWhere<T>, partialEntity: QueryDeepPartialEntity<T>): Promise<UpdateResult> {
		const userId = SessionStore.userId
		if (userId && 'lastUpdatedBy' in partialEntity) {
			partialEntity.lastUpdatedBy = userId
		}
		return super.update(criteria, partialEntity)
	}

	private assignAuditFields(entity: DeepPartial<T>, userId: string): void {
		if (entity.id) entity.lastUpdatedBy = userId
		else entity.createdBy = userId
	}
}
