import { Injectable } from '@angular/core';
import { isAnimatedGif, isAnimatedWebp } from '../../../profile-avatar/infrastructure/services/profile-avatar-image.service';

export interface ProcessedServerIcon {
  dataUrl: string;
  mime: string;
  size: number;
}

const SERVER_ICON_SIZE = 64;
const STATIC_ICON_CANDIDATES = [
  { mime: 'image/webp', quality: 0.82 },
  { mime: 'image/jpeg', quality: 0.82 },
  { mime: 'image/png' }
];

@Injectable({ providedIn: 'root' })
export class ServerIconImageService {
  async process(file: File): Promise<ProcessedServerIcon> {
    if (!file.type.startsWith('image/')) {
      throw new Error('Choose an image file.');
    }

    const objectUrl = URL.createObjectURL(file);

    try {
      const image = await this.loadImage(objectUrl);
      const isAnimated = await this.isAnimated(file);

      if (isAnimated && image.naturalWidth <= SERVER_ICON_SIZE && image.naturalHeight <= SERVER_ICON_SIZE) {
        const dataUrl = await this.readBlobAsDataUrl(file);

        return {
          dataUrl,
          mime: file.type || this.resolveMimeFromDataUrl(dataUrl),
          size: file.size
        };
      }

      return await this.renderStaticIcon(image);
    } finally {
      URL.revokeObjectURL(objectUrl);
    }
  }

  private async renderStaticIcon(image: HTMLImageElement): Promise<ProcessedServerIcon> {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');

    if (!context) {
      throw new Error('Canvas not supported.');
    }

    canvas.width = SERVER_ICON_SIZE;
    canvas.height = SERVER_ICON_SIZE;

    const scale = Math.max(SERVER_ICON_SIZE / image.naturalWidth, SERVER_ICON_SIZE / image.naturalHeight);
    const drawWidth = image.naturalWidth * scale;
    const drawHeight = image.naturalHeight * scale;
    const drawX = (SERVER_ICON_SIZE - drawWidth) / 2;
    const drawY = (SERVER_ICON_SIZE - drawHeight) / 2;

    context.clearRect(0, 0, SERVER_ICON_SIZE, SERVER_ICON_SIZE);
    context.imageSmoothingEnabled = true;
    context.imageSmoothingQuality = 'high';
    context.drawImage(image, drawX, drawY, drawWidth, drawHeight);

    const candidates = await Promise.all(
      STATIC_ICON_CANDIDATES.map(async (candidate) => {
        const blob = await this.canvasToBlob(canvas, candidate.mime, candidate.quality);
        const dataUrl = await this.readBlobAsDataUrl(blob);

        return {
          dataUrl,
          mime: blob.type || candidate.mime,
          size: blob.size
        };
      })
    );

    return candidates.reduce((smallest, candidate) => (candidate.size < smallest.size ? candidate : smallest));
  }

  private async isAnimated(file: File): Promise<boolean> {
    const mime = file.type.toLowerCase();

    if (mime !== 'image/gif' && mime !== 'image/webp') {
      return false;
    }

    const buffer = await file.arrayBuffer();

    return mime === 'image/gif' ? isAnimatedGif(buffer) : isAnimatedWebp(buffer);
  }

  private canvasToBlob(canvas: HTMLCanvasElement, type: string, quality?: number): Promise<Blob> {
    return new Promise((resolve, reject) => {
      canvas.toBlob(
        (blob) => {
          if (blob) {
            resolve(blob);
            return;
          }

          reject(new Error('Failed to render server image.'));
        },
        type,
        quality
      );
    });
  }

  private readBlobAsDataUrl(blob: Blob): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = () => {
        if (typeof reader.result === 'string') {
          resolve(reader.result);
          return;
        }

        reject(new Error('Failed to encode server image.'));
      };

      reader.onerror = () => reject(reader.error ?? new Error('Failed to read server image.'));
      reader.readAsDataURL(blob);
    });
  }

  private loadImage(url: string): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const image = new Image();

      image.onload = () => resolve(image);
      image.onerror = () => reject(new Error('Failed to load server image.'));
      image.src = url;
    });
  }

  private resolveMimeFromDataUrl(dataUrl: string): string {
    const match = /^data:([^;,]+)/.exec(dataUrl);

    return match?.[1] || 'image/webp';
  }
}
