import {
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { join } from 'node:path';
import { PrismaService } from '../../infrastructure/database/prisma.service';
import { AppConfigService } from '../config/config.service';
import { UploadPrepareRequestDto, UploadPrepareResponseDto, UploadSessionStatusResponseDto } from './uploads.dto';
import { UploadSessionStatus } from '@prisma/client';

@Injectable()
export class UploadsService {
  constructor(
    private readonly prismaService: PrismaService,
    private readonly configService: AppConfigService,
  ) {}

  async prepare(
    body: UploadPrepareRequestDto,
  ): Promise<UploadPrepareResponseDto> {
    if (body.sizeBytes > this.configService.maxUploadSizeBytes) {
      throw new UnprocessableEntityException('Upload exceeds the configured maximum size.');
    }

    const device = await this.prismaService.device.findUnique({
      where: { id: body.deviceId },
    });

    if (!device) {
      throw new NotFoundException('Device not found');
    }

    const existingAsset = await this.prismaService.audioAsset.findUnique({
      where: { sha256: body.sha256 },
    });

    if (existingAsset) {
      return {
        status: 'exists',
      };
    }

    const uploadSession = await this.prismaService.uploadSession.create({
      data: {
        deviceId: body.deviceId,
        expectedSha256: body.sha256,
        expectedSizeBytes: BigInt(body.sizeBytes),
        receivedBytes: BigInt(0),
        tempStoragePath: join('incoming', `${body.sha256}.part`),
        status: UploadSessionStatus.READY_TO_UPLOAD,
        expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
      },
    });

    return {
      status: 'upload_required',
      uploadId: uploadSession.id,
      nextOffset: 0,
    };
  }

  async getStatus(uploadId: string): Promise<UploadSessionStatusResponseDto> {
    const uploadSession = await this.prismaService.uploadSession.findUnique({
      where: { id: uploadId },
    });

    if (!uploadSession) {
      throw new NotFoundException('Upload session not found');
    }

    return {
      uploadId: uploadSession.id,
      status: uploadSession.status,
      receivedBytes: uploadSession.receivedBytes.toString(),
      expectedSizeBytes: uploadSession.expectedSizeBytes.toString(),
      nextOffset: uploadSession.receivedBytes.toString(),
    };
  }
}
