import { writeFile, mkdir } from 'fs/promises';
import { join } from 'path';

function getS3Config() {
  const endpoint = process.env.S3_ENDPOINT;
  const bucket = process.env.S3_BUCKET;
  const region = process.env.S3_REGION || 'us-east-1';
  const accessKeyId = process.env.S3_ACCESS_KEY;
  const secretAccessKey = process.env.S3_SECRET_KEY;
  const publicUrl = process.env.S3_PUBLIC_URL;
  return { endpoint, bucket, region, accessKeyId, secretAccessKey, publicUrl };
}

async function uploadToS3(
  buffer: Buffer,
  filename: string,
  folder: string,
  mimeType: string
): Promise<string> {
  const { S3Client, PutObjectCommand } = await import('@aws-sdk/client-s3');
  const cfg = getS3Config();
  if (!cfg.bucket || !cfg.accessKeyId || !cfg.secretAccessKey) {
    throw new Error('S3 storage is enabled but S3_BUCKET, S3_ACCESS_KEY, or S3_SECRET_KEY is not set.');
  }

  const client = new S3Client({
    region: cfg.region,
    ...(cfg.endpoint ? { endpoint: cfg.endpoint, forcePathStyle: true } : {}),
    credentials: {
      accessKeyId: cfg.accessKeyId,
      secretAccessKey: cfg.secretAccessKey,
    },
  });

  const key = `${folder}/${filename}`;
  await client.send(
    new PutObjectCommand({
      Bucket: cfg.bucket,
      Key: key,
      Body: buffer,
      ContentType: mimeType,
    })
  );

  if (cfg.publicUrl) {
    return `${cfg.publicUrl.replace(/\/$/, '')}/${key}`;
  }
  if (cfg.endpoint) {
    return `${cfg.endpoint.replace(/\/$/, '')}/${cfg.bucket}/${key}`;
  }
  return `https://${cfg.bucket}.s3.${cfg.region}.amazonaws.com/${key}`;
}

function sanitizePath(segment: string): string {
  if (/\.\.|[/\\]/.test(segment) || segment.startsWith('.')) {
    throw new Error('Path traversal detected: invalid path segment');
  }
  if (!segment) throw new Error('Invalid path segment');
  return segment;
}

async function uploadToLocalDisk(
  buffer: Buffer,
  filename: string,
  folder: string
): Promise<string> {
  const safeFolder = sanitizePath(folder);
  const safeFilename = sanitizePath(filename);
  const uploadDir = join(process.cwd(), 'public', 'uploads', safeFolder);
  await mkdir(uploadDir, { recursive: true });
  await writeFile(join(uploadDir, safeFilename), buffer);
  return `/uploads/${safeFolder}/${safeFilename}`;
}

export async function uploadFile(
  buffer: Buffer,
  filename: string,
  folder: string,
  mimeType: string
): Promise<string> {
  const driver = (process.env.STORAGE_DRIVER || 'local').toLowerCase();
  if (driver === 's3') {
    return uploadToS3(buffer, filename, folder, mimeType);
  }
  return uploadToLocalDisk(buffer, filename, folder);
}
