import * as grpc from "@grpc/grpc-js";
import { ServiceError } from "@grpc/grpc-js";
import { IronPdfServiceClient } from "../../generated_proto/ironpdfengineproto/IronPdfService";
import { ChromeApplyStampRequestStreamP } from "../../generated_proto/ironpdfengineproto/ChromeApplyStampRequestStreamP";
import { EmptyResultP__Output } from "../../generated_proto/ironpdfengineproto/EmptyResultP";
import { chunkBuffer, chunkString, handleEmptyResultP__Output } from "../util";
import {
	BarcodeStampOptions,
	HtmlStampOptions,
	ImageStampOptions,
	TextStampOptions,
} from "../../../public/stamp";
import { PdfPageSelection, PdfPassword } from "../../../public/types";
import { Access } from "../../access";
import { getPageInfo } from "../pdfium/page";
import {
	BarcodeStampOptionsToProto,
	HtmlStampOptionsToProto,
	ImageStampOptionsToProto,
	TextStampOptionsToProto,
} from "./converter";

function createStampStream(
	client: IronPdfServiceClient,
	resolve: (_: void) => void,
	reject: (errorMsg: string) => void
): grpc.ClientWritableStream<ChromeApplyStampRequestStreamP> {
	return client.Chrome_Stamp_ApplyStamp(
		(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 stampHtml(
	id: string,
	html: string,
	options?: {
		htmlStampOptions?: HtmlStampOptions | undefined;
		pageSelection?: PdfPageSelection | undefined;
		password?: PdfPassword | undefined;
	} | undefined
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();

	const pagesInfo = await getPageInfo(id);

	return new Promise(
		(resolve: (_: void) => void, reject: (errorMsg: string) => void) => {
			const stream: grpc.ClientWritableStream<ChromeApplyStampRequestStreamP> =
				createStampStream(client, resolve, reject);
			const info = HtmlStampOptionsToProto(
				id,
				pagesInfo,
				options?.password,
				options?.htmlStampOptions,
				options?.pageSelection
			);

			stream.write({
				info: info,
			});

			chunkString(html)?.forEach((htmlChunk) => {
				stream.write({
					stampValue: htmlChunk,
				});
			});
			stream.end();
		}
	);
}

export async function stampText(
	id: string,
	text: string,
	options?: {
		password?: PdfPassword | undefined;
		textStampOptions?: TextStampOptions | undefined;
		pageSelection?: PdfPageSelection | undefined;
	} | undefined
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	const pagesInfo = await getPageInfo(id);

	return new Promise(
		(resolve: (_: void) => void, reject: (errorMsg: string) => void) => {
			const stream: grpc.ClientWritableStream<ChromeApplyStampRequestStreamP> =
				createStampStream(client, resolve, reject);
			const info = TextStampOptionsToProto(
				id,
				pagesInfo,
				options?.password,
				options?.textStampOptions,
				options?.pageSelection
			);

			stream.write({
				info: info,
			});

			chunkString(text)?.forEach((textChunk) => {
				stream.write({
					stampValue: textChunk,
				});
			});
			stream.end();
		}
	);
}

export async function stampImage(
	id: string,
	imageBuffer: Buffer,
	options?: {
		password?: PdfPassword | undefined;
		imageStampOptions?: ImageStampOptions | undefined;
		pageSelection?: PdfPageSelection | undefined;
	} | undefined
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	const pagesInfo = await getPageInfo(id);
	return new Promise(
		(resolve: (_: void) => void, reject: (errorMsg: string) => void) => {
			const stream: grpc.ClientWritableStream<ChromeApplyStampRequestStreamP> =
				createStampStream(client, resolve, reject);
			const info = ImageStampOptionsToProto(
				id,
				pagesInfo,
				options?.password,
				options?.imageStampOptions,
				options?.pageSelection
			);

			stream.write({
				info: info,
			});

			chunkBuffer(imageBuffer)?.forEach((imageChunk) => {
				stream.write({
					stampImageBytes: imageChunk,
				});
			});
			stream.end();
		}
	);
}

export async function stampBarcode(
	id: string,
	barcodeValue: string,
	options?: {
		password?: PdfPassword | undefined;
		barcodeStampOptions?: BarcodeStampOptions | undefined;
		pageSelection?: PdfPageSelection | undefined;
	} | undefined
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	const pagesInfo = await getPageInfo(id);
	return new Promise(
		(resolve: (_: void) => void, reject: (errorMsg: string) => void) => {
			const stream: grpc.ClientWritableStream<ChromeApplyStampRequestStreamP> =
				createStampStream(client, resolve, reject);
			const info = BarcodeStampOptionsToProto(
				id,
				pagesInfo,
				options?.password,
				options?.barcodeStampOptions,
				options?.pageSelection
			);

			stream.write({
				info: info,
			});

			chunkString(barcodeValue)?.forEach((bv) => {
				stream.write({
					stampValue: bv,
				});
			});
			stream.end();
		}
	);
}
