import { Injectable } from '@nestjs/common';
import { createHash } from 'node:crypto';
import { promises as fsp } from 'node:fs';
import { createWriteStream, statSync, existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { pipeline } from 'node:stream/promises';
import { ATT_TMP_DIR } from 'src/time-and-attendance/attachments/config/attachment.config';
import { casPathFor, getAbsolutePath } from 'src/time-and-attendance/attachments/cas.util';

export type SaveResult = { sha256: string, storage_path: string, size: number };

@Injectable()
export class DiskStorageService {
    // async exists(storagePathRel: string) {
    //     try {
    //         statSync(getAbsolutePath(storagePathRel));
    //         return true;
    //     } catch {
    //         return false;
    //     }
    // }

    //adds file and hash it
    async saveStreamAndHash(input: NodeJS.ReadableStream): Promise<SaveResult> {
        // 1- writing in ROOT:/_tmp while streaming and hashing
        const tmpDir = ATT_TMP_DIR();
        await fsp.mkdir(tmpDir, { recursive: true });
        const tmpPath = join(tmpDir, `up_${Date.now()}_${Math.random().toString(36).slice(2)}`);

        const hash = createHash('sha256');
        const tmpOut = createWriteStream(tmpPath);
        input.on('data', (chunk) => hash.update(chunk));
        await pipeline(input, tmpOut); //await end of writing stream

        const sha = hash.digest('hex');
        const rel = casPathFor(sha);
        const finalAbs = getAbsolutePath(rel);

        // 2- is there is no destination => move (atomic renaming on the same volume)
        if (!existsSync(finalAbs)) {
            await fsp.mkdir(dirname(finalAbs), { recursive: true });
            try {
                await fsp.rename(tmpPath, finalAbs);
            } catch (e) {
                //if someone is faster and used the same hash
                if (existsSync(finalAbs)) {
                    await fsp.rm(tmpPath, { force: true });
                } else {
                    throw e;
                }
            }
        } else {
            //remove duplicata if already exists
            await fsp.rm(tmpPath, { force: true });
        }

        const size = statSync(finalAbs).size;
        return { sha256: sha, storage_path: rel, size };
    }
}