import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { UserWork, ListStatus } from './user-work.entity'; import { WorkService } from '../work/work.service'; import { UserService } from '../user/user.service'; const XP_ADD_WORK = 10; const XP_PROGRESS = 5; const XP_COMPLETE = 100; const XP_SCORE = 15; @Injectable() export class ListService { constructor( @InjectRepository(UserWork) private readonly userWorkRepo: Repository, private readonly workService: WorkService, private readonly userService: UserService, ) {} async getUserList(userId: number, status?: ListStatus) { const where: any = { userId }; if (status) where.status = status; return this.userWorkRepo.find({ where, relations: ['work'], order: { updatedAt: 'DESC' }, }); } async addToList( userId: number, anilistId: number, status: ListStatus, ): Promise { const work = await this.workService.findOrCreateFromAniList(anilistId); const existing = await this.userWorkRepo.findOne({ where: { userId, workId: work.id }, }); if (existing) { existing.status = status; return this.userWorkRepo.save(existing); } const userWork = this.userWorkRepo.create({ userId, workId: work.id, status, progress: 0, }); const saved = await this.userWorkRepo.save(userWork); await this.userService.addXp(userId, XP_ADD_WORK); return saved; } async updateProgress( userId: number, userWorkId: number, progress: number, ): Promise { const uw = await this.userWorkRepo.findOne({ where: { id: userWorkId, userId }, relations: ['work'], }); if (!uw) throw new NotFoundException('Entry not found'); const oldProgress = uw.progress; uw.progress = progress; const total = uw.work.totalEpisodes || uw.work.totalChapters; if (total && progress >= total && uw.status !== ListStatus.COMPLETED) { uw.status = ListStatus.COMPLETED; uw.completedAt = new Date(); await this.userService.addXp(userId, XP_COMPLETE); } const saved = await this.userWorkRepo.save(uw); if (progress > oldProgress) { const delta = progress - oldProgress; await this.userService.addXp(userId, delta * XP_PROGRESS); } return saved; } async updateStatus( userId: number, userWorkId: number, status: ListStatus, ): Promise { const uw = await this.userWorkRepo.findOne({ where: { id: userWorkId, userId }, }); if (!uw) throw new NotFoundException('Entry not found'); const wasAlreadyCompleted = uw.status === ListStatus.COMPLETED; uw.status = status; if (status === ListStatus.COMPLETED && !wasAlreadyCompleted) { uw.completedAt = uw.completedAt || new Date(); await this.userService.addXp(userId, XP_COMPLETE); } return this.userWorkRepo.save(uw); } async setScore( userId: number, userWorkId: number, score: number, ): Promise { const uw = await this.userWorkRepo.findOne({ where: { id: userWorkId, userId }, }); if (!uw) throw new NotFoundException('Entry not found'); const hadScore = uw.score !== null; uw.score = score; const saved = await this.userWorkRepo.save(uw); if (!hadScore) { await this.userService.addXp(userId, XP_SCORE); } return saved; } async removeFromList(userId: number, userWorkId: number): Promise { const uw = await this.userWorkRepo.findOne({ where: { id: userWorkId, userId }, }); if (!uw) throw new NotFoundException('Entry not found'); await this.userWorkRepo.remove(uw); } }