import { Module } from '@nestjs/common';
import { TypeOrmModule, getDataSourceToken } from '@nestjs/typeorm';
import type { DataSource } from 'typeorm';

import { AchievementOrmEntity } from '@adapters/achievement/persistence/typeorm/entities/AchievementOrmEntity';
import { UserAchievementOrmEntity } from '@adapters/achievement/persistence/typeorm/entities/UserAchievementOrmEntity';
import { TypeOrmAchievementRepository } from '@adapters/achievement/persistence/typeorm/repositories/TypeOrmAchievementRepository';
import { AchievementOrmMapper } from '@adapters/achievement/persistence/typeorm/mappers/AchievementOrmMapper';

import { ACHIEVEMENT_REPOSITORY_TOKEN } from '../achievement/AchievementPersistenceTokens';
import { Achievement, AchievementCategory } from '@core/identity/domain/entities/Achievement';
import { UserAchievement } from '@core/identity/domain/entities/UserAchievement';

// Adapter to convert between domain repository interface and application use case interface
class AchievementRepositoryAdapter {
  constructor(private readonly typeOrmRepo: TypeOrmAchievementRepository) {}

  // Application use case interface methods
  async save(achievement: Achievement): Promise<void> {
    await this.typeOrmRepo.createAchievement(achievement);
  }

  async findById(id: string): Promise<Achievement | null> {
    return await this.typeOrmRepo.findAchievementById(id);
  }

  // Delegate all other methods to the underlying TypeOrm repository
  async findAchievementById(id: string): Promise<Achievement | null> {
    return await this.typeOrmRepo.findAchievementById(id);
  }

  async findAllAchievements(): Promise<Achievement[]> {
    return await this.typeOrmRepo.findAllAchievements();
  }

  async findAchievementsByCategory(category: AchievementCategory): Promise<Achievement[]> {
    return await this.typeOrmRepo.findAchievementsByCategory(category);
  }

  async createAchievement(achievement: Achievement): Promise<Achievement> {
    return await this.typeOrmRepo.createAchievement(achievement);
  }

  async findUserAchievementById(id: string): Promise<UserAchievement | null> {
    return await this.typeOrmRepo.findUserAchievementById(id);
  }

  async findUserAchievementsByUserId(userId: string): Promise<UserAchievement[]> {
    return await this.typeOrmRepo.findUserAchievementsByUserId(userId);
  }

  async findUserAchievementByUserAndAchievement(userId: string, achievementId: string): Promise<UserAchievement | null> {
    return await this.typeOrmRepo.findUserAchievementByUserAndAchievement(userId, achievementId);
  }

  async hasUserEarnedAchievement(userId: string, achievementId: string): Promise<boolean> {
    return await this.typeOrmRepo.hasUserEarnedAchievement(userId, achievementId);
  }

  async createUserAchievement(userAchievement: UserAchievement): Promise<UserAchievement> {
    return await this.typeOrmRepo.createUserAchievement(userAchievement);
  }

  async updateUserAchievement(userAchievement: UserAchievement): Promise<UserAchievement> {
    return await this.typeOrmRepo.updateUserAchievement(userAchievement);
  }

  async getAchievementLeaderboard(limit: number): Promise<{ userId: string; points: number; count: number }[]> {
    return await this.typeOrmRepo.getAchievementLeaderboard(limit);
  }

  async getUserAchievementStats(userId: string): Promise<{
    total: number;
    points: number;
    byCategory: Record<AchievementCategory, number>;
  }> {
    return await this.typeOrmRepo.getUserAchievementStats(userId);
  }
}

const typeOrmFeatureImports = [TypeOrmModule.forFeature([AchievementOrmEntity, UserAchievementOrmEntity])];

@Module({
  imports: [...typeOrmFeatureImports],
  providers: [
    { provide: AchievementOrmMapper, useFactory: () => new AchievementOrmMapper() },
    {
      provide: ACHIEVEMENT_REPOSITORY_TOKEN,
      useFactory: (dataSource: DataSource, mapper: AchievementOrmMapper) =>
        new AchievementRepositoryAdapter(new TypeOrmAchievementRepository(dataSource, mapper)),
      inject: [getDataSourceToken(), AchievementOrmMapper],
    },
  ],
  exports: [ACHIEVEMENT_REPOSITORY_TOKEN],
})
export class PostgresAchievementPersistenceModule {}