// eslint-disable-next-line import/no-extraneous-dependencies
import lottie from 'lottie-web/build/player/lottie_light';
// eslint-disable-next-line import/no-extraneous-dependencies
import { inflate } from 'pako';
// eslint-disable-next-line import/no-extraneous-dependencies
import GIF from 'gif.js';

/** Animation item type from lottie-web */
type AnimationItem = ReturnType<typeof lottie.loadAnimation>;

/** Default GIF frame rate */
const GIF_FPS = 15;

/** Default GIF quality (1-20, lower is better but slower) */
const GIF_QUALITY = 10;

/** Maximum dimension for GIF output */
const MAX_GIF_SIZE = 256;

/** Transparency key color (magenta) - this color becomes transparent in the GIF */
const TRANSPARENT_COLOR = 0xFF00FF;

/**
 * Decompress a TGS file (gzipped Lottie JSON)
 * @param tgsData - The compressed TGS data
 * @returns The Lottie animation JSON object
 */
function decompressTgs(tgsData: ArrayBuffer): object {
  const decompressed = inflate(new Uint8Array(tgsData), { to: 'string' });
  return JSON.parse(decompressed);
}

/**
 * Render a Lottie animation to GIF
 * @param animationData - The Lottie JSON animation data
 * @param width - Output width
 * @param height - Output height
 * @returns Promise resolving to GIF blob
 */
async function renderLottieToGif(
  animationData: object,
  width: number,
  height: number
): Promise<Blob> {
  return new Promise((resolve, reject) => {
    // Create a hidden container for rendering
    const container = document.createElement('div');
    container.style.cssText = `
      position: fixed;
      left: -9999px;
      top: -9999px;
      width: ${width}px;
      height: ${height}px;
      overflow: hidden;
    `;
    document.body.appendChild(container);

    // Create canvas for capturing frames
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');

    if (!ctx) {
      document.body.removeChild(container);
      reject(new Error('Failed to get canvas context'));
      return;
    }

    // Initialize Lottie animation
    let animation: AnimationItem;
    try {
      animation = lottie.loadAnimation({
        container,
        renderer: 'svg',
        loop: false,
        autoplay: false,
        animationData,
      });
    } catch (error) {
      document.body.removeChild(container);
      reject(error);
      return;
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const workerScript = (GIF as any).getWorkerScript?.() || '/gif.worker.js';

    // Initialize GIF encoder with transparency support
    // Use magenta as the transparent color key
    const gif = new GIF({
      workers: 2,
      quality: GIF_QUALITY,
      width,
      height,
      workerScript,
      transparent: TRANSPARENT_COLOR,
    });

    const { totalFrames } = animation;
    const duration = animation.getDuration(false) || 3; // Default 3 seconds if duration unknown
    const frameDelay = 1000 / GIF_FPS;
    const framesToCapture = Math.ceil(duration * GIF_FPS);

    let capturedFrames = 0;

    /**
     * Finalize and render the GIF
     */
    const finishGif = () => {
      gif.on('finished', (gifBlob: Blob) => {
        animation.destroy();
        document.body.removeChild(container);
        resolve(gifBlob);
      });

      gif.on('error', (error: Error) => {
        animation.destroy();
        document.body.removeChild(container);
        reject(error);
      });

      gif.render();
    };

    /**
     * Capture a single frame from the animation
     */
    const captureFrame = (frameIndex: number) => {
      // Calculate the animation frame to seek to
      const progress = frameIndex / framesToCapture;
      const animFrame = Math.floor(progress * totalFrames);
      
      animation.goToAndStop(animFrame, true);

      // Get the SVG content and render to canvas
      const svgElement = container.querySelector('svg');
      if (svgElement) {
        const svgData = new XMLSerializer().serializeToString(svgElement);
        const img = new Image();
        const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
        const url = URL.createObjectURL(svgBlob);

        img.onload = () => {
          // Fill with transparent key color first
          ctx.fillStyle = '#FF00FF';
          ctx.fillRect(0, 0, width, height);
          ctx.drawImage(img, 0, 0, width, height);
          URL.revokeObjectURL(url);

          gif.addFrame(ctx, { copy: true, delay: frameDelay, dispose: 2 });
          capturedFrames += 1;

          if (capturedFrames < framesToCapture) {
            captureFrame(capturedFrames);
          } else {
            finishGif();
          }
        };

        img.onerror = () => {
          URL.revokeObjectURL(url);
          capturedFrames += 1;
          if (capturedFrames < framesToCapture) {
            captureFrame(capturedFrames);
          } else {
            finishGif();
          }
        };

        img.src = url;
      } else {
        capturedFrames += 1;
        if (capturedFrames < framesToCapture) {
          captureFrame(capturedFrames);
        } else {
          finishGif();
        }
      }
    };

    // Start capturing frames
    captureFrame(0);
  });
}

/**
 * Convert a TGS (Telegram animated sticker) to GIF
 * @param tgsBlob - The TGS file blob
 * @param targetWidth - Desired output width (will maintain aspect ratio)
 * @param targetHeight - Desired output height (will maintain aspect ratio)
 * @returns Promise resolving to GIF blob
 */
export async function tgsToGif(
  tgsBlob: Blob,
  targetWidth = 256,
  targetHeight = 256
): Promise<Blob> {
  // eslint-disable-next-line no-console
  console.log('tgsToGif: Starting conversion, blob size:', tgsBlob.size);
  const arrayBuffer = await tgsBlob.arrayBuffer();
  const animationData = decompressTgs(arrayBuffer);

  // Calculate output dimensions (maintain aspect ratio, cap at MAX_GIF_SIZE)
  const width = Math.min(targetWidth, MAX_GIF_SIZE);
  const height = Math.min(targetHeight, MAX_GIF_SIZE);

  return renderLottieToGif(animationData, width, height);
}

/**
 * Convert a WEBM video sticker to GIF
 * @param webmBlob - The WEBM file blob
 * @param targetWidth - Desired output width
 * @param targetHeight - Desired output height
 * @returns Promise resolving to GIF blob
 */
export async function webmToGif(
  webmBlob: Blob,
  targetWidth = 256,
  targetHeight = 256
): Promise<Blob> {
  // eslint-disable-next-line no-console
  console.log('webmToGif: Starting conversion, blob size:', webmBlob.size);
  return new Promise((resolve, reject) => {
    const video = document.createElement('video');
    video.muted = true;
    video.playsInline = true;
    
    const canvas = document.createElement('canvas');
    const width = Math.min(targetWidth, MAX_GIF_SIZE);
    const height = Math.min(targetHeight, MAX_GIF_SIZE);
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');

    if (!ctx) {
      reject(new Error('Failed to get canvas context'));
      return;
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const workerScript = (GIF as any).getWorkerScript?.() || '/gif.worker.js';

    // Initialize GIF encoder with transparency support
    // Use magenta as the transparent color key
    const gif = new GIF({
      workers: 2,
      quality: GIF_QUALITY,
      width,
      height,
      workerScript,
      transparent: TRANSPARENT_COLOR,
    });

    const url = URL.createObjectURL(webmBlob);
    video.src = url;
    video.load();

    video.onloadedmetadata = () => {
      // eslint-disable-next-line no-console
      console.log('webmToGif: Video metadata loaded, duration:', video.duration);
      const { duration } = video;
      const frameDelay = 1000 / GIF_FPS;
      const totalFrames = Math.ceil(duration * GIF_FPS);
      let currentFrame = 0;

      /**
       * Capture frames from video
       */
      const captureNextFrame = () => {
        if (currentFrame >= totalFrames) {
          // eslint-disable-next-line no-console
          console.log('webmToGif: All frames captured, rendering GIF');
          URL.revokeObjectURL(url);
          gif.on('finished', (blob: Blob) => {
            // eslint-disable-next-line no-console
            console.log('webmToGif: GIF rendered, size:', blob.size);
            resolve(blob);
          });
          gif.on('error', (err) => {
            // eslint-disable-next-line no-console
            console.error('webmToGif: GIF render error:', err);
            reject(err);
          });
          gif.render();
          return;
        }

        const time = (currentFrame / totalFrames) * duration;
        video.currentTime = time;
      };

      video.onseeked = () => {
        // Fill with transparent key color first
        ctx.fillStyle = '#FF00FF';
        ctx.fillRect(0, 0, width, height);
        ctx.drawImage(video, 0, 0, width, height);
        gif.addFrame(ctx, { copy: true, delay: frameDelay, dispose: 2 });
        currentFrame += 1;
        captureNextFrame();
      };

      captureNextFrame();
    };

    video.onerror = (e) => {
      // eslint-disable-next-line no-console
      console.error('webmToGif: Video load error:', e, video.error);
      URL.revokeObjectURL(url);
      reject(new Error(`Failed to load video: ${video.error?.message || 'unknown error'}`));
    };

    // Add timeout in case video never loads
    setTimeout(() => {
      if (!video.readyState) {
        // eslint-disable-next-line no-console
        console.error('webmToGif: Video load timeout');
        URL.revokeObjectURL(url);
        reject(new Error('Video load timeout'));
      }
    }, 30000);
  });
}

/**
 * Check if a file is a TGS (animated Telegram sticker)
 * @param mimeType - The file MIME type
 * @param filePath - The file path
 * @returns True if TGS
 */
export function isTgsFile(mimeType: string, filePath: string): boolean {
  return mimeType === 'application/x-tgsticker' || filePath.endsWith('.tgs');
}

/**
 * Check if a file is a WEBM video sticker
 * @param mimeType - The file MIME type
 * @param filePath - The file path
 * @returns True if WEBM
 */
export function isWebmFile(mimeType: string, filePath: string): boolean {
  return mimeType === 'video/webm' || filePath.endsWith('.webm');
}
