import "server-only";
import { mkdir } from "node:fs/promises";
import path from "node:path";
import { randomBytes } from "node:crypto";

export const STORAGE_ROOT = path.join(process.cwd(), "storage");
export const PUBLIC_ROOT = path.join(process.cwd(), "public");

export const MAX_FILE_BYTES = 15 * 1024 * 1024; // 15 MB
export const MAX_AVATAR_BYTES = 4 * 1024 * 1024; // 4 MB

const EXT_BY_MIME: Record<string, string> = {
  "image/png": "png",
  "image/jpeg": "jpg",
  "image/webp": "webp",
  "image/gif": "gif",
  "audio/webm": "webm",
  "audio/ogg": "ogg",
  "audio/mpeg": "mp3",
  "audio/mp4": "m4a",
  "audio/wav": "wav",
  "application/pdf": "pdf",
  "text/plain": "txt",
  "application/zip": "zip",
};

const IMAGE_MIME = new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
const AUDIO_MIME = new Set([
  "audio/webm",
  "audio/ogg",
  "audio/mpeg",
  "audio/mp4",
  "audio/wav",
]);

export type AttachmentKind = "IMAGE" | "AUDIO" | "FILE";

export function kindForMime(mime: string): AttachmentKind {
  if (IMAGE_MIME.has(mime)) return "IMAGE";
  if (AUDIO_MIME.has(mime)) return "AUDIO";
  return "FILE";
}

export function isImageMime(mime: string): boolean {
  return IMAGE_MIME.has(mime);
}

export function extForMime(mime: string): string {
  return EXT_BY_MIME[mime] ?? "bin";
}

export function isAllowedUploadMime(mime: string): boolean {
  return mime in EXT_BY_MIME;
}

/** Eindeutiger, sicherer Basisdateiname (keine Pfad-Bestandteile). */
export function makeStoredName(mime: string): string {
  return `${randomBytes(16).toString("hex")}.${extForMime(mime)}`;
}

/** Verhindert Path-Traversal: nur erlaubte Basisdateinamen akzeptieren. */
export function isSafeStoredName(name: string): boolean {
  return /^[a-f0-9]{32}\.[a-z0-9]{2,5}$/.test(name);
}

export function chatDir(channelId: string): string {
  return path.join(STORAGE_ROOT, "chat", channelId);
}

export async function ensureDir(dir: string): Promise<void> {
  await mkdir(dir, { recursive: true });
}

// ── Bild-Uploads (Profil- & Kanalbilder) ──────────────────────────────────
// Wichtig: Uploads liegen NICHT in `public/`. Next.js liest das public-
// Verzeichnis beim Serverstart ein — nach dem Start hinzugefügte Dateien
// werden nicht ausgeliefert (404), bis der Server neu startet. Deshalb werden
// Uploads unter `storage/uploads/…` abgelegt und über die Route
// `/uploads/[...path]` zur Laufzeit von der Platte gestreamt.
export const UPLOADS_ROOT = path.join(STORAGE_ROOT, "uploads");

/** Bekannte Upload-Bereiche (= erstes Pfadsegment unter /uploads/). */
export const UPLOAD_BUCKETS = ["avatars", "channels"] as const;
export type UploadBucket = (typeof UPLOAD_BUCKETS)[number];

export async function uploadDir(bucket: UploadBucket): Promise<string> {
  const dir = path.join(UPLOADS_ROOT, bucket);
  await ensureDir(dir);
  return dir;
}

export async function avatarDir(): Promise<string> {
  return uploadDir("avatars");
}

/** Erlaubte Dateinamen für Bild-Uploads (kein Path-Traversal). */
export function isSafeUploadName(name: string): boolean {
  return /^[a-zA-Z0-9_-]{1,120}\.(png|jpg|jpeg|webp|gif)$/.test(name);
}

const MIME_BY_EXT: Record<string, string> = {
  png: "image/png",
  jpg: "image/jpeg",
  jpeg: "image/jpeg",
  webp: "image/webp",
  gif: "image/gif",
};

export function mimeForExt(name: string): string {
  const ext = name.split(".").pop()?.toLowerCase() ?? "";
  return MIME_BY_EXT[ext] ?? "application/octet-stream";
}

/**
 * Mögliche Speicherorte einer Upload-Datei — zuerst das neue Storage-
 * Verzeichnis, danach `public/uploads` (Altbestand aus früheren Versionen).
 */
export function uploadCandidatePaths(bucket: string, name: string): string[] {
  return [
    path.join(UPLOADS_ROOT, bucket, name),
    path.join(PUBLIC_ROOT, "uploads", bucket, name),
  ];
}
