/** Local-disk driver — the MVP default. Keys are date-sharded to keep dirs small. */
import { createReadStream, createWriteStream } from 'node:fs';
import { mkdir, rm, stat } from 'node:fs/promises';
import { dirname, join, normalize, resolve, sep } from 'node:path';
import { pipeline } from 'node:stream/promises';
import type { Readable } from 'node:stream';
import { env } from '../config/env.js';
import { signFileUrl } from './signing.js';
import type { PutOptions, StorageDriver } from './driver.js';

const ROOT = resolve(process.cwd(), env.STORAGE_LOCAL_DIR);

/** Guards against `../` traversal in a storage key. */
function safePath(key: string): string {
  const clean = normalize(key).replace(/^(\.\.(\/|\\|$))+/, '');
  const full = resolve(ROOT, clean);
  if (!full.startsWith(ROOT + sep) && full !== ROOT) {
    throw new Error(`Refusing to access key outside storage root: ${key}`);
  }
  return full;
}

export class LocalDriver implements StorageDriver {
  readonly name = 'local';

  async put(stream: Readable, opts: PutOptions): Promise<{ bytes: number }> {
    const full = safePath(opts.key);
    await mkdir(dirname(full), { recursive: true });

    let bytes = 0;
    stream.on('data', (chunk: Buffer) => {
      bytes += chunk.length;
    });

    try {
      await pipeline(stream, createWriteStream(full));
    } catch (err) {
      await rm(full, { force: true }).catch(() => undefined);
      throw err;
    }
    return { bytes };
  }

  async get(key: string) {
    const full = safePath(key);
    const info = await stat(full);
    return { stream: createReadStream(full) as Readable, size: info.size };
  }

  async delete(key: string): Promise<void> {
    await rm(safePath(key), { force: true });
  }

  async exists(key: string): Promise<boolean> {
    try {
      await stat(safePath(key));
      return true;
    } catch {
      return false;
    }
  }

  async downloadUrl(key: string, ttlSeconds: number, filename?: string): Promise<string> {
    const { exp, sig } = signFileUrl('GET', key, ttlSeconds);
    const params = new URLSearchParams({ key, exp: String(exp), sig });
    if (filename) params.set('name', filename);
    return `${env.PUBLIC_URL}/api/files/download?${params.toString()}`;
  }
}

export const localRoot = () => ROOT;
export { safePath as localSafePath, join as pathJoin };
