import { MatrixClient } from 'matrix-js-sdk';
import { fetch as tauriFetch } from '@tauri-apps/plugin-http';
import { AccountDataEvent } from '../../types/matrix/accountData';
import { ImageUsage, PackContent, PackImages } from '../plugins/custom-emoji/types';
import { getAccountData } from './room';
import { tgsToGif, webmToGif } from './animatedStickerConverter';

/** Telegram Bot API base URL */
const TELEGRAM_API = 'https://api.telegram.org/bot';

/** Telegram file download base URL */
const TELEGRAM_FILE_API = 'https://api.telegram.org/file/bot';

/**
 * Telegram sticker info from Bot API
 */
interface TelegramSticker {
  file_id: string;
  file_unique_id: string;
  type: string;
  width: number;
  height: number;
  is_animated: boolean;
  is_video: boolean;
  emoji?: string;
  set_name?: string;
  thumbnail?: {
    file_id: string;
    width: number;
    height: number;
  };
}

/**
 * Telegram sticker set info from Bot API
 */
interface TelegramStickerSet {
  name: string;
  title: string;
  sticker_type: string;
  contains_masks: boolean;
  stickers: TelegramSticker[];
  thumbnail?: {
    file_id: string;
    width: number;
    height: number;
  };
}

/**
 * Telegram API response wrapper
 */
interface TelegramResponse<T> {
  ok: boolean;
  result?: T;
  description?: string;
}

/**
 * Progress callback for import operation
 */
export type ImportProgressCallback = (current: number, total: number, status: string) => void;

/**
 * Extract pack name from a Telegram sticker URL
 * Supports: t.me/addstickers/PackName, https://t.me/addstickers/PackName
 * @param url - The Telegram sticker pack URL
 * @returns The pack name, or null if invalid
 */
export function extractPackName(url: string): string | null {
  const trimmed = url.trim();
  
  // Direct pack name (no URL)
  if (/^[a-zA-Z0-9_]+$/.test(trimmed)) {
    return trimmed;
  }
  
  // URL format: t.me/addstickers/PackName or https://t.me/addstickers/PackName
  const match = trimmed.match(/(?:https?:\/\/)?t\.me\/addstickers\/([a-zA-Z0-9_]+)/i);
  return match ? match[1] : null;
}

/**
 * Fetch sticker set info from Telegram Bot API
 * @param botToken - Telegram Bot API token
 * @param setName - Name of the sticker set
 * @returns Sticker set info
 */
export async function fetchStickerSet(
  botToken: string,
  setName: string
): Promise<TelegramStickerSet> {
  const response = await fetch(
    `${TELEGRAM_API}${botToken}/getStickerSet?name=${encodeURIComponent(setName)}`
  );
  
  if (!response.ok) {
    throw new Error(`Failed to fetch sticker set: ${response.statusText}`);
  }
  
  const data: TelegramResponse<TelegramStickerSet> = await response.json();
  
  if (!data.ok || !data.result) {
    throw new Error(data.description || 'Failed to fetch sticker set');
  }
  
  return data.result;
}

/**
 * Get file path for a Telegram file
 * @param botToken - Telegram Bot API token
 * @param fileId - Telegram file ID
 * @returns File path for download
 */
async function getFilePath(botToken: string, fileId: string): Promise<string> {
  const response = await fetch(`${TELEGRAM_API}${botToken}/getFile?file_id=${fileId}`);
  
  if (!response.ok) {
    throw new Error(`Failed to get file path: ${response.statusText}`);
  }
  
  const data: TelegramResponse<{ file_path: string }> = await response.json();
  
  if (!data.ok || !data.result) {
    throw new Error(data.description || 'Failed to get file path');
  }
  
  return data.result.file_path;
}

/**
 * Download a file from Telegram
 * Uses Tauri HTTP plugin to bypass CORS restrictions
 * @param botToken - Telegram Bot API token
 * @param filePath - File path from getFile
 * @returns File blob
 */
async function downloadFile(botToken: string, filePath: string): Promise<Blob> {
  const response = await tauriFetch(`${TELEGRAM_FILE_API}${botToken}/${filePath}`);
  
  if (!response.ok) {
    throw new Error(`Failed to download file: ${response.statusText}`);
  }
  
  return response.blob();
}

/**
 * Upload a blob to Matrix and get the mxc:// URL
 * @param mx - Matrix client
 * @param blob - File blob to upload
 * @param filename - Filename for the upload
 * @returns mxc:// URL
 */
async function uploadToMatrix(
  mx: MatrixClient,
  blob: Blob,
  filename: string
): Promise<string> {
  const response = await mx.uploadContent(blob, {
    name: filename,
    type: blob.type || 'image/webp',
  });
  
  return response.content_uri;
}

/**
 * Determine MIME type from file path
 * @param filePath - Telegram file path
 * @returns MIME type string
 */
function getMimeType(filePath: string): string {
  if (filePath.endsWith('.webp')) return 'image/webp';
  if (filePath.endsWith('.webm')) return 'video/webm';
  if (filePath.endsWith('.tgs')) return 'application/x-tgsticker';
  if (filePath.endsWith('.png')) return 'image/png';
  if (filePath.endsWith('.jpg') || filePath.endsWith('.jpeg')) return 'image/jpeg';
  return 'image/webp';
}

/**
 * Import a Telegram sticker pack to Matrix
 * @param mx - Matrix client
 * @param botToken - Telegram Bot API token
 * @param packUrl - Telegram sticker pack URL or name
 * @param onProgress - Progress callback
 * @returns The created pack content
 */
export async function importTelegramStickerPack(
  mx: MatrixClient,
  botToken: string,
  packUrl: string,
  onProgress?: ImportProgressCallback
): Promise<{ packId: string; content: PackContent }> {
  // Extract pack name from URL
  const packName = extractPackName(packUrl);
  if (!packName) {
    throw new Error('Invalid Telegram sticker pack URL');
  }
  
  onProgress?.(0, 1, 'Fetching sticker pack info...');
  
  // Fetch sticker set from Telegram
  const stickerSet = await fetchStickerSet(botToken, packName);
  
  const total = stickerSet.stickers.length;
  const images: PackImages = {};
  
  // Download and upload each sticker sequentially
  // (Using sequential await is intentional to avoid overwhelming APIs)
  for (let i = 0; i < stickerSet.stickers.length; i += 1) {
    const sticker = stickerSet.stickers[i];
    const isAnimated = sticker.is_animated;
    const isVideo = sticker.is_video;
    
    let stickerType = 'static';
    if (isAnimated) stickerType = 'animated';
    else if (isVideo) stickerType = 'video';
    onProgress?.(i + 1, total, `Importing ${stickerType} sticker ${i + 1}/${total}...`);
    
    try {
      // Get file path from Telegram
      // eslint-disable-next-line no-await-in-loop
      const filePath = await getFilePath(botToken, sticker.file_id);
      
      // Download the sticker
      // eslint-disable-next-line no-await-in-loop
      let blob = await downloadFile(botToken, filePath);
      
      // Generate a shortcode from emoji or index
      const shortcode = sticker.emoji
        ? `${packName}_${sticker.emoji.codePointAt(0)?.toString(16) || i}`
        : `${packName}_${i}`;
      
      let finalMimeType = getMimeType(filePath);
      let extension = 'webp';
      
      // Convert animated stickers to GIF
      if (isAnimated && filePath.endsWith('.tgs')) {
        onProgress?.(i + 1, total, `Converting animated sticker ${i + 1}/${total} to GIF...`);
        // eslint-disable-next-line no-await-in-loop
        blob = await tgsToGif(blob, sticker.width, sticker.height);
        finalMimeType = 'image/gif';
        extension = 'gif';
      } else if (isVideo && filePath.endsWith('.webm')) {
        onProgress?.(i + 1, total, `Converting video sticker ${i + 1}/${total} to GIF...`);
        // eslint-disable-next-line no-await-in-loop
        blob = await webmToGif(blob, sticker.width, sticker.height);
        finalMimeType = 'image/gif';
        extension = 'gif';
      }
      
      // Upload to Matrix
      // eslint-disable-next-line no-await-in-loop
      const mxcUrl = await uploadToMatrix(
        mx,
        blob,
        `${shortcode}.${extension}`
      );
      
      // Add to images
      images[shortcode] = {
        url: mxcUrl,
        body: sticker.emoji || shortcode,
        usage: [ImageUsage.Emoticon, ImageUsage.Sticker],
        info: {
          w: sticker.width,
          h: sticker.height,
          mimetype: finalMimeType,
          size: blob.size,
        },
      };
    } catch (err) {
      // Log error and continue with other stickers
      // eslint-disable-next-line no-console
      console.error(`Failed to import sticker ${i + 1}:`, err);
    }
  }
  
  if (Object.keys(images).length === 0) {
    throw new Error('Failed to import any stickers from the pack');
  }
  
  // eslint-disable-next-line no-console
  console.log(`Successfully imported ${Object.keys(images).length} stickers`);

  // Create pack content
  const packContent: PackContent = {
    pack: {
      display_name: stickerSet.title,
      usage: [ImageUsage.Emoticon, ImageUsage.Sticker],
    },
    images,
  };
  
  // Generate a unique pack ID
  const packId = `telegram_${packName}`;
  
  // Get existing user emotes or create new
  const existingEvent = getAccountData(mx, AccountDataEvent.PoniesUserEmotes);
  const existingEmotes = existingEvent?.getContent() as PackContent | undefined;
  
  // Merge with existing pack if any
  const mergedContent: PackContent = {
    pack: existingEmotes?.pack || { usage: [ImageUsage.Emoticon, ImageUsage.Sticker] },
    images: {
      ...existingEmotes?.images,
      ...images,
    },
  };
  
  // Save to user account data
  // eslint-disable-next-line no-console
  console.log('Saving merged pack to account data:', AccountDataEvent.PoniesUserEmotes);
  // eslint-disable-next-line no-console
  console.log('Merged content:', JSON.stringify(mergedContent, null, 2));
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  await mx.setAccountData(AccountDataEvent.PoniesUserEmotes as any, mergedContent as any);
  // eslint-disable-next-line no-console
  console.log('Pack saved successfully');
  
  // Force the account data event to be emitted for UI refresh
  const updatedEvent = getAccountData(mx, AccountDataEvent.PoniesUserEmotes);
  // eslint-disable-next-line no-console
  console.log('Updated account data after save:', updatedEvent?.getContent());
  
  onProgress?.(total, total, 'Import complete!');
  
  return { packId, content: packContent };
}
