import { Injectable } from "@nestjs/common";
import { allowedMimes } from "src/time-and-attendance/attachments/upload.config";
import { UploadMetaAttachmentsDto } from "src/time-and-attendance/attachments/dtos/upload-meta-attachments.dto";
import { Readable } from "node:stream";
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";
import { fileTypeFromBuffer } from "file-type";
import { Result } from "src/common/errors/result-error.factory";
import { DiskStorageService } from "src/time-and-attendance/attachments/services/disk-storage.service";
import { VariantsQueue } from "src/time-and-attendance/attachments/services/variants.queue";

@Injectable()
export class AttachmentUploadService {
    constructor(
        private readonly prisma: PrismaPostgresService,
        private readonly disk: DiskStorageService,
        private readonly variantsQ: VariantsQueue,
    ) { }

    async uploadAttachment(file?: Express.Multer.File, meta?: UploadMetaAttachmentsDto): Promise<Result<any, string>> {
        if (!file) return { success: false, error: 'FILE_NOT_FOUND' };

        //magic detection using binary signature
        const kind = await fileTypeFromBuffer(file.buffer).catch(() => null);
        const detected_mime = kind?.mime || file.mimetype || 'application/octet-stream';

        //strict whitelist
        if (!allowedMimes().includes(detected_mime)) {
            return { success: false, error: 'INVALID_ATTACHMENT_TYPE' };
        }

        //Saving FS (hash + CAS + unDupes)
        const stream = Readable.from(file.buffer);
        const { sha256, storage_path, size } = await this.disk.saveStreamAndHash(stream);

        const now = new Date();
        const attachment = await this.prisma.client.$transaction(async (tx) => {
            //upsert blob: +1 ref
            await tx.blobs.upsert({
                where: { sha256 },
                create: {
                    sha256,
                    storage_path: storage_path,
                    size,
                    mime: detected_mime,
                    refcount: 1,
                    created_at: now,
                },
                update: { //only increment, does not change the storage path
                    refcount: { increment: 1 },
                    mime: detected_mime, //update mime and size to keep last image
                    size,
                },
            });

            const att = await tx.attachments.create({
                data: {
                    sha256,
                    owner_type: meta?.owner_type ?? 'EXPENSE',
                    owner_id: meta?.owner_id ?? 'unknown',
                    original_name: file.originalname,
                    status: 'ACTIVE',
                    retention_policy: (meta?.retention_policy ?? 'EXPENSE_7Y') as any,
                    created_by: meta?.created_by ?? 'system',
                    created_at: now,
                },
            });
            return att;
        });

        await this.variantsQ.enqueue(attachment.id, detected_mime);

        return {
            success: true,
            data: {
                ok: true,
                id: attachment.id,
                sha256,
                storage_path: storage_path,
                size,
                mime: detected_mime,
                original_name: file.originalname,
                owner_type: attachment.owner_type,
                owner_id: attachment.owner_id,
            }
        };
    }
}