import { ipcMain, clipboard } from 'electron';
import { promises as fsPromises } from 'fs';
import * as fs from 'fs';
import * as path from 'path';
import { IPC } from './shared-with-frontend/ipc-events.const';
import { createValidatedHandler } from './ipc-handler-wrapper';
import {
  EXTENSION_MIME_TYPES,
  SUPPORTED_IMAGE_EXTENSIONS,
} from './shared-with-frontend/mime-type-mapping.const';

interface ClipboardImageMeta {
  id: string;
  mimeType: string;
  createdAt: number;
  size: number;
}

/**
 * Ensures the clipboard-images directory exists.
 */
const ensureDir = (dirPath: string): void => {
  if (!fs.existsSync(dirPath)) {
    fs.mkdirSync(dirPath, { recursive: true });
  }
};

/**
 * Gets the MIME type from file extension.
 */
const getMimeFromExt = (ext: string): string => {
  const extLower = ext.toLowerCase();
  return (
    EXTENSION_MIME_TYPES[extLower as keyof typeof EXTENSION_MIME_TYPES] || 'image/png'
  );
};

/**
 * Finds the image file by ID (checking various extensions).
 * @electron-only This function is only available in the Electron main process.
 */
const findImageFile = (basePath: string, imageId: string): string | null => {
  for (const ext of SUPPORTED_IMAGE_EXTENSIONS) {
    const filePath = path.join(basePath, `${imageId}${ext}`);
    if (fs.existsSync(filePath)) {
      return filePath;
    }
  }

  return null;
};

export const initClipboardImageHandlers = (): void => {
  // Save clipboard image
  ipcMain.handle(
    IPC.CLIPBOARD_IMAGE_SAVE,
    createValidatedHandler(
      async ({
        basePath,
        fileName,
        base64Data,
      }: {
        basePath: string;
        fileName: string;
        base64Data: string;
        mimeType: string;
      }) => {
        ensureDir(basePath);

        const filePath = path.join(basePath, fileName);
        const buffer = Buffer.from(base64Data, 'base64');

        await fsPromises.writeFile(filePath, new Uint8Array(buffer));

        return filePath;
      },
      { validatePath: true },
    ),
  );

  // Load clipboard image
  ipcMain.handle(
    IPC.CLIPBOARD_IMAGE_LOAD,
    createValidatedHandler(
      async ({ basePath, imageId }: { basePath: string; imageId: string }) => {
        const filePath = findImageFile(basePath, imageId);
        if (!filePath) {
          return null;
        }

        const buffer = await fsPromises.readFile(filePath);
        const ext = path.extname(filePath);
        const mimeType = getMimeFromExt(ext);

        return {
          base64: buffer.toString('base64'),
          mimeType,
        };
      },
      { validatePath: true, errorValue: null },
    ),
  );

  // Delete clipboard image
  ipcMain.handle(
    IPC.CLIPBOARD_IMAGE_DELETE,
    createValidatedHandler(
      async ({ basePath, imageId }: { basePath: string; imageId: string }) => {
        const filePath = findImageFile(basePath, imageId);
        if (!filePath) {
          return false;
        }

        await fsPromises.unlink(filePath);
        return true;
      },
      { validatePath: true, errorValue: false },
    ),
  );

  // List clipboard images
  ipcMain.handle(
    IPC.CLIPBOARD_IMAGE_LIST,
    createValidatedHandler(
      async ({ basePath }: { basePath: string }) => {
        if (!fs.existsSync(basePath)) {
          return [];
        }

        const files = await fsPromises.readdir(basePath);
        const imageExtensions = new Set(SUPPORTED_IMAGE_EXTENSIONS);

        const images: ClipboardImageMeta[] = [];

        for (const file of files) {
          const ext = path.extname(file).toLowerCase();
          if (!imageExtensions.has(ext)) {
            continue;
          }

          const filePath = path.join(basePath, file);
          const stats = await fsPromises.stat(filePath);
          const id = path.basename(file, ext);

          images.push({
            id,
            mimeType: getMimeFromExt(ext),
            createdAt: stats.birthtimeMs,
            size: stats.size,
          });
        }

        return images;
      },
      { validatePath: true, errorValue: [] },
    ),
  );

  // Get clipboard image file path
  ipcMain.handle(
    IPC.CLIPBOARD_IMAGE_GET_PATH,
    createValidatedHandler(
      async ({ basePath, imageId }: { basePath: string; imageId: string }) => {
        return findImageFile(basePath, imageId);
      },
      { validatePath: true, errorValue: null },
    ),
  );

  // Copy image file from clipboard to clipboard-images directory
  ipcMain.handle(
    IPC.CLIPBOARD_COPY_IMAGE_FILE,
    createValidatedHandler(
      async ({ basePath, filePath }: { basePath: string; filePath: string }) => {
        ensureDir(basePath);

        // Generate unique ID
        const id = Date.now().toString(36) + Math.random().toString(36).substring(2);
        const ext = path.extname(filePath).toLowerCase();
        // SECURITY: only copy actual image files. `filePath` is renderer-supplied;
        // without this a plugin/XSS could copy an arbitrary file (e.g. a secret)
        // into the images dir and read it back via CLIPBOARD_IMAGE_LOAD.
        if (!SUPPORTED_IMAGE_EXTENSIONS.includes(ext)) {
          throw new Error('Invalid source image');
        }
        const destFileName = `${id}${ext}`;
        const destPath = path.join(basePath, destFileName);

        // Copy the file
        await fsPromises.copyFile(filePath, destPath);

        // Get file stats
        const stats = await fsPromises.stat(destPath);
        const mimeType = getMimeFromExt(ext);

        return {
          id,
          mimeType,
          size: stats.size,
          createdAt: Date.now(),
        };
      },
      { validatePath: true, errorValue: null },
    ),
  );

  // Read image directly from clipboard
  ipcMain.handle(
    IPC.CLIPBOARD_READ_IMAGE,
    createValidatedHandler(
      async ({ basePath }: { basePath: string }) => {
        const image = clipboard.readImage();

        if (image.isEmpty()) {
          return null;
        }

        ensureDir(basePath);

        // Generate unique ID
        const id = Date.now().toString(36) + Math.random().toString(36).substring(2);
        const fileName = `${id}.png`;
        const filePath = path.join(basePath, fileName);

        // Save as PNG
        const pngBuffer = image.toPNG();
        await fsPromises.writeFile(filePath, new Uint8Array(pngBuffer));

        const stats = await fsPromises.stat(filePath);

        return {
          id,
          mimeType: 'image/png',
          size: stats.size,
          createdAt: Date.now(),
        };
      },
      { validatePath: true, errorValue: null },
    ),
  );

  ipcMain.handle(
    IPC.CLIPBOARD_GET_FILE_PATHS,
    createValidatedHandler(
      async () => {
        const filePaths: string[] = [];

        // Note: Electron's clipboard API on Windows doesn't reliably read file paths
        // when files are copied. clipboard.readImage() is used as fallback.

        // Try reading plain text (sometimes contains file paths)
        const plainText = clipboard.readText();
        if (plainText && plainText.startsWith('file://')) {
          const lines = plainText.split('\n');
          for (const line of lines) {
            const trimmed = line.trim();
            if (trimmed.startsWith('file://')) {
              let filePath = trimmed.substring(7);
              if (process.platform === 'win32' && filePath.startsWith('/')) {
                filePath = filePath.substring(1);
              }
              filePath = decodeURIComponent(filePath);
              if (process.platform === 'win32') {
                filePath = filePath.replace(/\//g, '\\');
              }
              filePaths.push(filePath);
            }
          }
        }

        // Filter to only existing image files
        const imageFiles = filePaths.filter((filePath) => {
          if (!fs.existsSync(filePath)) return false;
          const ext = path.extname(filePath).toLowerCase();
          return SUPPORTED_IMAGE_EXTENSIONS.includes(ext);
        });

        return imageFiles;
      },
      { errorValue: [] },
    ),
  );
};
