import "server-only";
import { randomBytes } from "crypto";
import { mkdir, writeFile } from "fs/promises";
import path from "path";
import { getS3Config } from "./settings";

/**
 * Image storage adapter.
 * With S3 configured (env: S3_BUCKET, S3_REGION, AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY,
 * optional S3_PUBLIC_URL for CloudFront/custom domains) images go to the bucket.
 * Without it, images land in <repo>/uploads and are served via /api/files/*.
 */

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

export const MAX_IMAGE_BYTES = 5 * 1024 * 1024;

export function extFromType(contentType: string): string | null {
  const found = Object.entries(EXT_TYPES).find(([, t]) => t === contentType.toLowerCase());
  return found?.[0] ?? (contentType.startsWith("image/") ? contentType.slice(6).split("+")[0] : null);
}

export async function saveImage(bytes: Uint8Array, contentType: string, prefix = "speakers"): Promise<string> {
  const ext = extFromType(contentType) ?? "jpg";
  const key = `${prefix}/${Date.now().toString(36)}-${randomBytes(4).toString("hex")}.${ext}`;

  const s3 = await getS3Config();
  if (s3) {
    const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3");
    const client = new S3Client({
      region: s3.region,
      ...(s3.accessKeyId && s3.secretAccessKey
        ? { credentials: { accessKeyId: s3.accessKeyId, secretAccessKey: s3.secretAccessKey } }
        : {}), // otherwise SDK default chain (env vars / instance role)
    });
    await client.send(
      new PutObjectCommand({
        Bucket: s3.bucket,
        Key: key,
        Body: bytes,
        ContentType: contentType,
        CacheControl: "public, max-age=31536000, immutable",
      })
    );
    const base = s3.publicUrl?.replace(/\/$/, "") ?? `https://${s3.bucket}.s3.${s3.region}.amazonaws.com`;
    return `${base}/${key}`;
  }

  const abs = path.join(process.cwd(), "uploads", key);
  await mkdir(path.dirname(abs), { recursive: true });
  await writeFile(abs, bytes);
  return `/api/files/${key}`;
}

/** Save an uploaded <input type="file"> image; returns its URL, or null if empty/invalid. */
export async function saveUploadedImage(file: unknown, prefix = "speakers"): Promise<string | null> {
  if (!(file instanceof File) || file.size === 0) return null;
  if (file.size > MAX_IMAGE_BYTES) throw new Error("Image is larger than 5 MB.");
  if (!file.type.startsWith("image/")) throw new Error("Only image files are allowed.");
  return saveImage(new Uint8Array(await file.arrayBuffer()), file.type, prefix);
}

/** Download a remote image (e.g. LinkedIn profile photo) into our storage. */
export async function fetchAndStoreImage(url: string, prefix = "speakers"): Promise<string | null> {
  try {
    const res = await fetch(url, { signal: AbortSignal.timeout(8000) });
    if (!res.ok) return null;
    const type = res.headers.get("content-type") ?? "";
    if (!type.startsWith("image/")) return null;
    const bytes = new Uint8Array(await res.arrayBuffer());
    if (bytes.byteLength === 0 || bytes.byteLength > MAX_IMAGE_BYTES) return null;
    return await saveImage(bytes, type.split(";")[0], prefix);
  } catch {
    return null;
  }
}
