import fetch from "node-fetch";
import sizeOf from "image-size";

import { PosterInfo } from "./types";

// Same limits as the legacy Eventer app (src/server.ts): 15MB cap, jpeg/png
// only, and a squareness check since event posters are displayed as square
// thumbnails on the site.
export const MAX_FILE_SIZE_IN_BYTES = 15 * 1_000_000;
export const ALLOWED_CONTENT_TYPES = ["image/png", "image/jpeg"];
const SQUARE_TOLERANCE = 0.2;

export class PosterValidationError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "PosterValidationError";
  }
}

function fail(message: string): never {
  throw new PosterValidationError(message);
}

export interface FetchedPoster {
  buffer: Buffer;
  filename: string;
}

// Downloads the poster from poster.sourceUrl and validates it against the
// same rules the legacy Eventer app enforced client-upload-side: file type,
// size, and aspect ratio. Throws PosterValidationError on any violation.
export async function fetchAndValidatePoster(poster: PosterInfo): Promise<FetchedPoster> {
  if (!ALLOWED_CONTENT_TYPES.includes(poster.contentType)) {
    fail(`poster.contentType must be one of ${ALLOWED_CONTENT_TYPES.join(", ")}`);
  }

  const response = await fetch(poster.sourceUrl);
  if (!response.ok) {
    fail(`Failed to download poster from sourceUrl (status ${response.status})`);
  }

  const declaredContentType = response.headers.get("content-type");
  if (declaredContentType && !ALLOWED_CONTENT_TYPES.includes(declaredContentType.split(";")[0].trim())) {
    fail(`poster sourceUrl served unexpected Content-Type "${declaredContentType}"`);
  }

  const buffer = await response.buffer();
  if (buffer.length > MAX_FILE_SIZE_IN_BYTES) {
    fail(`poster exceeds the ${MAX_FILE_SIZE_IN_BYTES / 1_000_000}MB size limit`);
  }

  const dimensions = sizeOf(buffer);
  if (!dimensions.width || !dimensions.height) {
    fail("could not determine poster image dimensions");
  }
  const ratio = dimensions.height / dimensions.width;
  if (ratio < 1 - SQUARE_TOLERANCE || ratio > 1 + SQUARE_TOLERANCE) {
    fail("poster must be approximately square");
  }

  return { buffer, filename: poster.filename };
}
