import { spawnSync } from "child_process";
import { randomUUID } from "crypto";
import { readFileSync, unlinkSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import { clipboard } from "./clipboard-native.js";
import { loadPhoton } from "./photon.js";
const SUPPORTED_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
const DEFAULT_LIST_TIMEOUT_MS = 1000;
const DEFAULT_READ_TIMEOUT_MS = 3000;
const DEFAULT_POWERSHELL_TIMEOUT_MS = 5000;
const DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
export function isWaylandSession(env = process.env) {
    return Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === "wayland";
}
function baseMimeType(mimeType) {
    return mimeType.split(";")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();
}
export function extensionForImageMimeType(mimeType) {
    switch (baseMimeType(mimeType)) {
        case "image/png":
            return "png";
        case "image/jpeg":
            return "jpg";
        case "image/webp":
            return "webp";
        case "image/gif":
            return "gif";
        default:
            return null;
    }
}
function selectPreferredImageMimeType(mimeTypes) {
    const normalized = mimeTypes
        .map((t) => t.trim())
        .filter(Boolean)
        .map((t) => ({ raw: t, base: baseMimeType(t) }));
    for (const preferred of SUPPORTED_IMAGE_MIME_TYPES) {
        const match = normalized.find((t) => t.base === preferred);
        if (match) {
            return match.raw;
        }
    }
    const anyImage = normalized.find((t) => t.base.startsWith("image/"));
    return anyImage?.raw ?? null;
}
function isSupportedImageMimeType(mimeType) {
    const base = baseMimeType(mimeType);
    return SUPPORTED_IMAGE_MIME_TYPES.some((t) => t === base);
}
/**
 * Convert unsupported image formats to PNG using Photon.
 * Returns null if conversion is unavailable or fails.
 */
async function convertToPng(bytes) {
    const photon = await loadPhoton();
    if (!photon) {
        return null;
    }
    try {
        const image = photon.PhotonImage.new_from_byteslice(bytes);
        try {
            return image.get_bytes();
        }
        finally {
            image.free();
        }
    }
    catch {
        return null;
    }
}
function runCommand(command, args, options) {
    const timeoutMs = options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS;
    const maxBufferBytes = options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;
    const result = spawnSync(command, args, {
        timeout: timeoutMs,
        maxBuffer: maxBufferBytes,
        env: options?.env,
    });
    if (result.error) {
        return { ok: false, stdout: Buffer.alloc(0) };
    }
    if (result.status !== 0) {
        return { ok: false, stdout: Buffer.alloc(0) };
    }
    const stdout = Buffer.isBuffer(result.stdout)
        ? result.stdout
        : Buffer.from(result.stdout ?? "", typeof result.stdout === "string" ? "utf-8" : undefined);
    return { ok: true, stdout };
}
function readClipboardImageViaWlPaste() {
    const list = runCommand("wl-paste", ["--list-types"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
    if (!list.ok) {
        return null;
    }
    const types = list.stdout
        .toString("utf-8")
        .split(/\r?\n/)
        .map((t) => t.trim())
        .filter(Boolean);
    const selectedType = selectPreferredImageMimeType(types);
    if (!selectedType) {
        return null;
    }
    const data = runCommand("wl-paste", ["--type", selectedType, "--no-newline"]);
    if (!data.ok || data.stdout.length === 0) {
        return null;
    }
    return { bytes: data.stdout, mimeType: baseMimeType(selectedType) };
}
function isWSL(env = process.env) {
    if (env.WSL_DISTRO_NAME || env.WSLENV) {
        return true;
    }
    try {
        const release = readFileSync("/proc/version", "utf-8");
        return /microsoft|wsl/i.test(release);
    }
    catch {
        return false;
    }
}
/**
 * On WSL, the Linux clipboard (Wayland/X11) does not receive image data from
 * Windows screenshots (Win+Shift+S). PowerShell can access the Windows clipboard
 * directly, so we use it as a fallback.
 */
function readClipboardImageViaPowerShell() {
    const tmpFile = join(tmpdir(), `pi-wsl-clip-${randomUUID()}.png`);
    try {
        const winPathResult = runCommand("wslpath", ["-w", tmpFile], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
        if (!winPathResult.ok) {
            return null;
        }
        const winPath = winPathResult.stdout.toString("utf-8").trim();
        if (!winPath) {
            return null;
        }
        const psQuotedWinPath = winPath.replaceAll("'", "''");
        const psScript = [
            "Add-Type -AssemblyName System.Windows.Forms",
            "Add-Type -AssemblyName System.Drawing",
            `$path = '${psQuotedWinPath}'`,
            "$img = [System.Windows.Forms.Clipboard]::GetImage()",
            "if ($img) { $img.Save($path, [System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'ok' } else { Write-Output 'empty' }",
        ].join("; ");
        const result = runCommand("powershell.exe", ["-NoProfile", "-Command", psScript], {
            timeoutMs: DEFAULT_POWERSHELL_TIMEOUT_MS,
        });
        if (!result.ok) {
            return null;
        }
        const output = result.stdout.toString("utf-8").trim();
        if (output !== "ok") {
            return null;
        }
        const bytes = readFileSync(tmpFile);
        if (bytes.length === 0) {
            return null;
        }
        return { bytes: new Uint8Array(bytes), mimeType: "image/png" };
    }
    catch {
        return null;
    }
    finally {
        try {
            unlinkSync(tmpFile);
        }
        catch {
            // Ignore cleanup errors.
        }
    }
}
function readClipboardImageViaXclip() {
    const targets = runCommand("xclip", ["-selection", "clipboard", "-t", "TARGETS", "-o"], {
        timeoutMs: DEFAULT_LIST_TIMEOUT_MS,
    });
    let candidateTypes = [];
    if (targets.ok) {
        candidateTypes = targets.stdout
            .toString("utf-8")
            .split(/\r?\n/)
            .map((t) => t.trim())
            .filter(Boolean);
    }
    const preferred = candidateTypes.length > 0 ? selectPreferredImageMimeType(candidateTypes) : null;
    const tryTypes = preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];
    for (const mimeType of tryTypes) {
        const data = runCommand("xclip", ["-selection", "clipboard", "-t", mimeType, "-o"]);
        if (data.ok && data.stdout.length > 0) {
            return { bytes: data.stdout, mimeType: baseMimeType(mimeType) };
        }
    }
    return null;
}
async function readClipboardImageViaNativeClipboard() {
    if (!clipboard || !clipboard.hasImage()) {
        return null;
    }
    const imageData = await clipboard.getImageBinary();
    if (!imageData || imageData.length === 0) {
        return null;
    }
    const bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);
    return { bytes, mimeType: "image/png" };
}
export async function readClipboardImage(options) {
    const env = options?.env ?? process.env;
    const platform = options?.platform ?? process.platform;
    if (env.TERMUX_VERSION) {
        return null;
    }
    let image = null;
    if (platform === "linux") {
        const wsl = isWSL(env);
        const wayland = isWaylandSession(env);
        if (wayland || wsl) {
            image = readClipboardImageViaWlPaste() ?? readClipboardImageViaXclip();
        }
        if (!image && wsl) {
            image = readClipboardImageViaPowerShell();
        }
        if (!image && !wayland) {
            image = await readClipboardImageViaNativeClipboard();
        }
    }
    else {
        image = await readClipboardImageViaNativeClipboard();
    }
    if (!image) {
        return null;
    }
    // Convert unsupported formats (e.g., BMP from WSLg) to PNG
    if (!isSupportedImageMimeType(image.mimeType)) {
        const pngBytes = await convertToPng(image.bytes);
        if (!pngBytes) {
            return null;
        }
        return { bytes: pngBytes, mimeType: "image/png" };
    }
    return image;
}
//# sourceMappingURL=clipboard-image.js.map