import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../infrastructure/database/prisma.service';
import { Prisma } from '@prisma/client';

@Injectable()
export class FilesRepository {
  constructor(private readonly prisma: PrismaService) {}

  async create(data: {
    userId: string;
    filename: string;
    mimeType?: string;
    storagePath: string;
    objectKey: string;
    bucket: string;
    sizeBytes: number;
    checksum?: string;
  }) {
    return this.prisma.uploadedFile.create({ data });
  }

  async findById(id: string) {
    return this.prisma.uploadedFile.findUnique({ where: { id } });
  }

  async findByObjectKey(objectKey: string) {
    return this.prisma.uploadedFile.findUnique({ where: { objectKey } });
  }

  async findByUserId(userId: string) {
    return this.prisma.uploadedFile.findMany({
      where: { userId },
      orderBy: { createdAt: 'desc' },
    });
  }

  async delete(id: string) {
    return this.prisma.uploadedFile.delete({ where: { id } });
  }

  async createUploadSession(data: {
    id: string;
    userId: string;
    objectKey: string;
    declaredSize: bigint;
    reservedBytes: bigint;
    quotaOperationId: string;
    expiresAt: Date;
  }) {
    return this.prisma.uploadSession.create({ data });
  }

  async findUploadSessionById(id: string) {
    return this.prisma.uploadSession.findUnique({
      where: { id },
      include: { uploadedFile: true },
    });
  }

  async findExpiredUploadSessions(now: Date, userId?: string) {
    return this.prisma.uploadSession.findMany({
      where: {
        status: { in: ['created', 'uploaded'] },
        expiresAt: { lte: now },
        ...(userId ? { userId } : {}),
      },
      select: { id: true, objectKey: true },
    });
  }

  async expireStaleUploadSessions(now: Date, userId?: string) {
    return this.prisma.uploadSession.updateMany({
      where: {
        status: { in: ['created', 'uploaded'] },
        expiresAt: { lte: now },
        ...(userId ? { userId } : {}),
      },
      data: { status: 'expired' },
    });
  }

  async markUploadSessionFailed(id: string) {
    return this.prisma.uploadSession.updateMany({
      where: { id, status: { in: ['created', 'uploaded'] } },
      data: { status: 'failed' },
    });
  }

  async sumCommittedStorageBytes(userId: string, tx?: Prisma.TransactionClient) {
    const client = tx ?? this.prisma;
    const result = await client.uploadedFile.aggregate({
      _sum: { sizeBytes: true },
      where: { userId },
    });
    return Number(result._sum.sizeBytes ?? 0);
  }

  async sumReservedStorageBytes(
    userId: string,
    now: Date,
    excludeSessionId?: string,
    tx?: Prisma.TransactionClient,
  ) {
    const client = tx ?? this.prisma;
    const result = await client.uploadSession.aggregate({
      _sum: { reservedBytes: true },
      where: {
        userId,
        status: { in: ['created', 'uploaded'] },
        expiresAt: { gt: now },
        ...(excludeSessionId ? { id: { not: excludeSessionId } } : {}),
      },
    });
    return Number(result._sum.reservedBytes ?? 0);
  }
}
