import "server-only";

import { randomBytes } from "crypto";
import { mkdir, readdir, readFile, unlink, writeFile } from "fs/promises";
import path from "path";
import { draftImageApiUrl as buildDraftImageApiUrl, MAX_DRAFT_IMAGES } from "./constants";

export { MAX_DRAFT_IMAGES };

const IMAGE_DIR = path.join(process.cwd(), "data", "draft-images");
const ALLOWED_MIME = new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);

export function draftImageAbsolutePath(imagePath: string): string {
  return path.join(IMAGE_DIR, path.basename(imagePath));
}

export function draftImageApiUrl(draftId: string, imagePath: string): string {
  return buildDraftImageApiUrl(draftId, imagePath);
}

export function draftImagePublicUrl(baseUrl: string, draftId: string, imagePath: string): string {
  return `${baseUrl.replace(/\/$/, "")}${buildDraftImageApiUrl(draftId, imagePath)}`;
}

export async function ensureDraftImageDir() {
  await mkdir(IMAGE_DIR, { recursive: true });
}

export function extensionForMime(mimeType: string): string {
  switch (mimeType) {
    case "image/png":
      return "png";
    case "image/webp":
      return "webp";
    case "image/gif":
      return "gif";
    default:
      return "jpg";
  }
}

export function validateDraftImageFile(file: File): string | null {
  if (!ALLOWED_MIME.has(file.type)) {
    return "僅支援 JPG、PNG、WebP、GIF";
  }
  if (file.size > 10 * 1024 * 1024) {
    return "圖片不得超過 10MB";
  }
  return null;
}

export function isDraftOwnedImagePath(draftId: string, imagePath: string): boolean {
  const base = path.basename(imagePath);
  return base.startsWith(`${draftId}-`) || base.startsWith(`${draftId}.`);
}

export function parseDraftImagePaths(draft: {
  imagePath?: string | null;
  imagePaths?: string | null;
}): string[] {
  if (draft.imagePaths) {
    try {
      const parsed = JSON.parse(draft.imagePaths) as unknown;
      if (Array.isArray(parsed)) {
        return parsed.filter((item): item is string => typeof item === "string");
      }
    } catch {
      // fall through
    }
  }
  if (draft.imagePath) return [draft.imagePath];
  return [];
}

export function serializeDraftImagePaths(paths: string[]): string | null {
  if (paths.length === 0) return null;
  return JSON.stringify(paths);
}

export function primaryDraftImagePath(draft: {
  imagePath?: string | null;
  imagePaths?: string | null;
}): string | null {
  const paths = parseDraftImagePaths(draft);
  return paths[0] ?? null;
}

export async function saveDraftImage(
  draftId: string,
  bytes: Uint8Array,
  mimeType: string
): Promise<string> {
  await ensureDraftImageDir();
  const filename = `${draftId}-${Date.now()}-${randomBytes(4).toString("hex")}.${extensionForMime(mimeType)}`;
  await writeFile(path.join(IMAGE_DIR, filename), bytes);
  return filename;
}

export async function readDraftImage(imagePath: string): Promise<{
  bytes: Buffer;
  mimeType: string;
} | null> {
  try {
    const bytes = await readFile(draftImageAbsolutePath(imagePath));
    const ext = path.extname(imagePath).slice(1).toLowerCase();
    const mimeType =
      ext === "png"
        ? "image/png"
        : ext === "webp"
          ? "image/webp"
          : ext === "gif"
            ? "image/gif"
            : "image/jpeg";
    return { bytes, mimeType };
  } catch {
    return null;
  }
}

export async function deleteDraftImageFile(imagePath: string | null | undefined) {
  if (!imagePath) return;
  try {
    await unlink(draftImageAbsolutePath(imagePath));
  } catch {
    // ignore missing file
  }
}

export async function deleteAllDraftImages(draftId: string) {
  await ensureDraftImageDir();
  const files = await readdir(IMAGE_DIR);
  await Promise.all(
    files
      .filter((file) => file.startsWith(`${draftId}-`) || file.startsWith(`${draftId}.`))
      .map((file) => unlink(path.join(IMAGE_DIR, file)).catch(() => undefined))
  );
}

export async function deleteDraftImages(paths: string[]) {
  await Promise.all(paths.map((imagePath) => deleteDraftImageFile(imagePath)));
}

export function syncDraftImageFields(paths: string[]) {
  return {
    imagePaths: serializeDraftImagePaths(paths),
    imagePath: paths[0] ?? null,
  };
}