import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
import path from 'node:path';

export const ANNOUNCEMENT_MAX_FILE_SIZE = 10 * 1024 * 1024;
export const ANNOUNCEMENT_ACCEPTED_TYPES = [
  'application/pdf',
  'image/jpeg',
  'image/jpg',
  'image/png',
  'application/msword',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  'application/vnd.ms-excel',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
] as const;

const PUBLIC_UPLOAD_ROOT = path.join(process.cwd(), 'public', 'uploads', 'announcements');

function sanitizeSegment(value: string) {
  return value.replace(/[^a-zA-Z0-9._-]/g, '-');
}

function ensureWithinUploadRoot(targetPath: string) {
  const normalizedRoot = path.resolve(PUBLIC_UPLOAD_ROOT);
  const normalizedTarget = path.resolve(targetPath);

  if (!normalizedTarget.startsWith(normalizedRoot)) {
    throw new Error('Invalid announcement storage path');
  }
}

export async function saveAnnouncementAttachment({
  file,
  organizationId,
  announcementId
}: {
  file: File;
  organizationId: string;
  announcementId: number | string;
}) {
  if (!ANNOUNCEMENT_ACCEPTED_TYPES.includes(file.type as (typeof ANNOUNCEMENT_ACCEPTED_TYPES)[number])) {
    throw new Error('Unsupported announcement file type');
  }

  if (file.size > ANNOUNCEMENT_MAX_FILE_SIZE) {
    throw new Error('Announcement file exceeds the maximum size of 10MB');
  }

  const extension = path.extname(file.name) || '.bin';
  const baseName = sanitizeSegment(path.basename(file.name, extension)) || 'attachment';
  const uniqueName = `${Date.now()}-${crypto.randomUUID()}-${baseName}${extension}`;
  const storageKey = path.join(sanitizeSegment(organizationId), String(announcementId), uniqueName);
  const absoluteDirectory = path.join(
    PUBLIC_UPLOAD_ROOT,
    sanitizeSegment(organizationId),
    String(announcementId)
  );
  const absolutePath = path.join(absoluteDirectory, uniqueName);

  ensureWithinUploadRoot(absolutePath);
  await mkdir(absoluteDirectory, { recursive: true });
  const bytes = Buffer.from(await file.arrayBuffer());
  await writeFile(absolutePath, bytes);

  return {
    storageKey: storageKey.replaceAll('\\', '/'),
    fileUrl: `/uploads/announcements/${storageKey.replaceAll('\\', '/')}`,
    fileName: file.name,
    fileType: file.type,
    fileSize: file.size
  };
}

export async function deleteStoredAnnouncementAttachment(storageKey: string) {
  const absolutePath = path.join(PUBLIC_UPLOAD_ROOT, storageKey);
  ensureWithinUploadRoot(absolutePath);

  try {
    await unlink(absolutePath);
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
      throw error;
    }
  }
}

export async function readStoredAnnouncementAttachment(storageKey: string) {
  const absolutePath = path.join(PUBLIC_UPLOAD_ROOT, storageKey);
  ensureWithinUploadRoot(absolutePath);

  return readFile(absolutePath);
}
