export interface CropArea {
  x: number;
  y: number;
  width: number;
  height: number;
}

export interface CompressOptions {
  maxDimension?: number;
  quality?: number;
  mimeType?: 'image/jpeg' | 'image/webp';
  rotation?: number;
}

export interface CropPreviewLayout {
  width: number;
  height: number;
  offsetX: number;
  offsetY: number;
  rotation: number;
}

const ALLOWED_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp']);

export function isAllowedAvatarType(file: File): boolean {
  return ALLOWED_TYPES.has(file.type);
}

export function normalizeRotation(rotation: number): number {
  return ((rotation % 360) + 360) % 360;
}

export function getOrientedDimensions(
  width: number,
  height: number,
  rotation = 0,
): { width: number; height: number } {
  const normalized = normalizeRotation(rotation);
  if (normalized === 90 || normalized === 270) {
    return { width: height, height: width };
  }
  return { width, height };
}

export function buildCropArea(
  imageWidth: number,
  imageHeight: number,
  zoom: number,
  rotation = 0,
): CropArea {
  const { width, height } = getOrientedDimensions(imageWidth, imageHeight, rotation);
  const safeZoom = Math.max(1, zoom);
  const side = Math.min(width, height) / safeZoom;
  const x = Math.max(0, (width - side) / 2);
  const y = Math.max(0, (height - side) / 2);
  return { x, y, width: side, height: side };
}

/** Maps image pixels to preview coordinates so the crop window matches the overlay. */
export function getCropPreviewLayout(
  imageWidth: number,
  imageHeight: number,
  zoom: number,
  containerPx: number,
  overlayInset = 0.08,
  rotation = 0,
): CropPreviewLayout {
  const normalizedRotation = normalizeRotation(rotation);
  const crop = buildCropArea(imageWidth, imageHeight, zoom, normalizedRotation);
  const overlaySize = containerPx * (1 - 2 * overlayInset);
  const scale = overlaySize / crop.width;
  const centerX = crop.x + crop.width / 2;
  const centerY = crop.y + crop.height / 2;

  return {
    width: imageWidth * scale,
    height: imageHeight * scale,
    offsetX: containerPx / 2 - centerX * scale,
    offsetY: containerPx / 2 - centerY * scale,
    rotation: normalizedRotation,
  };
}

export function loadImageFromFile(file: File): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const url = URL.createObjectURL(file);
    const image = new Image();
    image.onload = () => {
      URL.revokeObjectURL(url);
      resolve(image);
    };
    image.onerror = () => {
      URL.revokeObjectURL(url);
      reject(new Error('Unable to load image'));
    };
    image.src = url;
  });
}

export async function cropImageToBlob(
  image: HTMLImageElement,
  crop: CropArea,
  options: CompressOptions = {},
): Promise<Blob> {
  const maxDimension = options.maxDimension ?? 512;
  const quality = options.quality ?? 0.82;
  const mimeType = options.mimeType ?? 'image/webp';
  const rotation = normalizeRotation(options.rotation ?? 0);

  const side = Math.min(crop.width, crop.height);
  const srcW = image.naturalWidth;
  const srcH = image.naturalHeight;
  const oriented = getOrientedDimensions(srcW, srcH, rotation);

  const stage = document.createElement('canvas');
  stage.width = oriented.width;
  stage.height = oriented.height;
  const stageCtx = stage.getContext('2d');
  if (!stageCtx) {
    throw new Error('Canvas unavailable');
  }

  stageCtx.imageSmoothingEnabled = true;
  stageCtx.imageSmoothingQuality = 'high';
  stageCtx.translate(oriented.width / 2, oriented.height / 2);
  stageCtx.rotate((rotation * Math.PI) / 180);
  stageCtx.drawImage(image, -srcW / 2, -srcH / 2);

  const canvas = document.createElement('canvas');
  canvas.width = maxDimension;
  canvas.height = maxDimension;
  const ctx = canvas.getContext('2d');
  if (!ctx) {
    throw new Error('Canvas unavailable');
  }

  ctx.imageSmoothingEnabled = true;
  ctx.imageSmoothingQuality = 'high';
  ctx.drawImage(stage, crop.x, crop.y, side, side, 0, 0, maxDimension, maxDimension);

  return new Promise((resolve, reject) => {
    canvas.toBlob(
      (blob) => {
        if (!blob) {
          reject(new Error('Unable to compress image'));
          return;
        }
        resolve(blob);
      },
      mimeType,
      quality,
    );
  });
}

export async function blobToFile(blob: Blob, fileName: string): Promise<File> {
  return new File([blob], fileName, { type: blob.type });
}

export async function processAvatarFile(
  file: File,
  crop: CropArea,
  rotation = 0,
): Promise<File> {
  if (!isAllowedAvatarType(file)) {
    throw new Error('invalid_type');
  }

  const image = await loadImageFromFile(file);
  const blob = await cropImageToBlob(image, crop, { rotation });
  const extension = blob.type === 'image/jpeg' ? 'jpg' : 'webp';
  return blobToFile(blob, `avatar.${extension}`);
}
