import * as grpc from "@grpc/grpc-js";
import {ServiceError} from "@grpc/grpc-js";
import {IronPdfServiceClient} from "../../generated_proto/ironpdfengineproto/IronPdfService";
import {Access} from "../../access";
import {EmptyResultP__Output} from "../../generated_proto/ironpdfengineproto/EmptyResultP";
import {BytesResultStreamP__Output} from "../../generated_proto/ironpdfengineproto/BytesResultStreamP";
import {handleEmptyResultP__Output} from "../util";
import {PassThrough, Readable} from "stream";
import fs from "fs";

export async function compressImage(
	id: string,
	imageQuality: number,
	scaleToVisibleSize = false
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise(
		(resolve: () => void, reject: (errorMsg: string) => void) => {
			client.Pdfium_Compress_CompressImages(
				{
					document: {documentId: id},
					scaleToVisibleSize: scaleToVisibleSize,
					quality: imageQuality,
				},
				(
					err: ServiceError | null,
					value: EmptyResultP__Output | undefined
				) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handleEmptyResultP__Output(value, reject);
						resolve();
					}
				}
			);
		}
	);
}

export async function compressStructTree(
	id: string
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise(
		(resolve: () => void, reject: (errorMsg: string) => void) => {
			client.Pdfium_Compress_RemoveStructTree(
				{
					document: {documentId: id}
				},
				(
					err: ServiceError | null,
					value: EmptyResultP__Output | undefined
				) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handleEmptyResultP__Output(value, reject);
						resolve();
					}
				}
			);
		}
	);
}

export async function compressAndSaveAs(
	id: string,
	outputPath: string,
	imageQuality?: number
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();

	return new Promise(
		(resolve: () => void, reject: (errorMsg: string) => void) => {
			const request = {
					document: {documentId: id},
					outputPath: "",
					password: "",
					...(imageQuality !== undefined ? {jpeg: imageQuality} : {}),
				};
			const stream: grpc.ClientReadableStream<BytesResultStreamP__Output> =
				client.QPdf_Compression_CompressAndSaveAs(request);

			const buffers: Buffer[] = [];
			stream.on("data", (data: BytesResultStreamP__Output) => {
				if (data.exception) {
					reject(
						`${data.exception.message}/n${data.exception.remoteStackTrace}`
					);
				} else if (data.resultChunk) {
					buffers.push(data.resultChunk);
				}
			});

			stream.on("error", (err: Error) => {
				reject(`${err.name}/n${err.message}`);
			});

			stream.on("end", () => {
				fs.writeFileSync(outputPath, Buffer.concat(buffers));
				resolve();
			});
		}
	);
}

export async function compressInMemory(
	id: string,
	imageQuality?: number
): Promise<Buffer> {
	const client: IronPdfServiceClient = await Access.ensureConnection();

	return new Promise(
		(resolve: (_: Buffer) => void, reject: (errorMsg: string) => void) => {
			const request = {
					document: {documentId: id},
					outputPath: "",
					password: "",
					...(imageQuality !== undefined ? {jpeg: imageQuality} : {}),
				};
			const stream: grpc.ClientReadableStream<BytesResultStreamP__Output> =
				client.QPdf_Compression_CompressAndSaveAs(request);

			const buffers: Buffer[] = [];
			stream.on("data", (data: BytesResultStreamP__Output) => {
				if (data.exception) {
					reject(
						`${data.exception.message}/n${data.exception.remoteStackTrace}`
					);
				} else if (data.resultChunk) {
					buffers.push(data.resultChunk);
				}
			});

			stream.on("error", (err: Error) => {
				reject(`${err.name}/n${err.message}`);
			});

			stream.on("end", () => {
				resolve(Buffer.concat(buffers));
			});
		}
	);
}

export async function compressInMemoryStream(
	id: string,
	imageQuality?: number
): Promise<Readable> {
	const client: IronPdfServiceClient = await Access.ensureConnection();

	const passThrough = new PassThrough();

	const request = {
		document: {documentId: id},
		outputPath: "",
		password: "",
		...(imageQuality !== undefined ? {jpeg: imageQuality} : {}),
	};
	const stream: grpc.ClientReadableStream<BytesResultStreamP__Output> =
		client.QPdf_Compression_CompressAndSaveAs(request);

	stream.on("data", (data: BytesResultStreamP__Output) => {
		if (data.exception) {
			passThrough.destroy(
				new Error(`${data.exception.message}/n${data.exception.remoteStackTrace}`)
			);
		} else if (data.resultChunk) {
			passThrough.write(data.resultChunk);
		}
	});

	stream.on("error", (err: Error) => {
		passThrough.destroy(err);
	});

	stream.on("end", () => {
		passThrough.end();
	});

	return passThrough;
}
