import { ServiceError } from "@grpc/grpc-js";
import fs from "fs";
import { Buffer } from "buffer";
import { DigitalSignature } from "../../../public/signature";
import { IronPdfServiceClient } from "../../generated_proto/ironpdfengineproto/IronPdfService";
import { Access } from "../../access";
import { PdfiumSignResultP__Output } from "../../generated_proto/ironpdfengineproto/PdfiumSignResultP";
import { chunkBuffer, handleRemoteException } from "../util";
import { _ironpdfengineproto_PdfiumSignRequestStreamP_InfoP } from "../../generated_proto/ironpdfengineproto/PdfiumSignRequestStreamP";
import { PdfiumGetSignatureCountResultP__Output } from "../../generated_proto/ironpdfengineproto/PdfiumGetSignatureCountResultP";

export async function signPdf(
	id: string,
	signature: DigitalSignature
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	const existing_sigs = await getSignatureCount(id);
	return new Promise(
		(resolve: () => void, reject: (errorMsg: string) => void) => {
			const stream = client.Pdfium_Signature_Sign(
				(
					err: ServiceError | null,
					value: PdfiumSignResultP__Output | undefined
				) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						if (value?.exception) {
							handleRemoteException(value.exception, reject);
						}
						resolve();
					}
				}
			);

			const info: _ironpdfengineproto_PdfiumSignRequestStreamP_InfoP = {
				document: { documentId: id },
				password: signature.certificatePassword ?? "",
				signingLocation: signature.signingLocation ?? "",
				signingReason: signature.signingReason ?? "",
				signaturePermission: { enumValue: 0 },
				timeStampUrl: signature.timeStampUrl ?? "",
				pageIndex:signature.signatureImage?.SignatureImagePageIndex??0,
				internalName: "Signature"+ (1 + existing_sigs + 1),
				signatureImageW:signature.signatureImage?.SignatureImagePosition?.width??100,
				signatureImageH:signature.signatureImage?.SignatureImagePosition?.height??100,
				signatureImageX:signature.signatureImage?.SignatureImagePosition?.x??0,
				signatureImageY:signature.signatureImage?.SignatureImagePosition?.y??0,
			};

			const time = signature.signatureDate?.getTime();
			if (time) {
				info.signatureDate = {
					nanos: (time % 1000) * 1e6,
					seconds: time / 1000,
				};
			}

			stream.write({ info: info });

			let certBuffer: Buffer | undefined = undefined;

			if (signature.certificateBuffer) {
				certBuffer = signature.certificateBuffer;
			} else if (signature.certificatePath) {
				certBuffer = fs.readFileSync(signature.certificatePath);
			}

			if (certBuffer) {
				chunkBuffer(certBuffer).forEach((chunk) => {
					stream.write({ certificateFileBytesChunk: chunk });
				});
			}

			let imageBuffer: Buffer | undefined = undefined;

			if (signature.signatureImage?.SignatureImageBuffer) {
				imageBuffer = signature.signatureImage?.SignatureImageBuffer;
			} else if (signature.signatureImage?.SignatureImagePath) {
				imageBuffer = fs.readFileSync(
					signature.signatureImage?.SignatureImagePath
				);
			}

			if (imageBuffer) {
				chunkBuffer(imageBuffer).forEach((chunk) => {
					stream.write({ signatureImageChunk: chunk });
				});
			}

			stream.end();
		}
	);
}

export async function getSignatureCount(id: string): Promise<number> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise(
		(resolve: (_: number) => void, reject: (errorMsg: string) => void) => {
			client.Pdfium_Signature_GetSignatureCount(
				{ document: { documentId: id } },
				(
					err: ServiceError | null,
					value: PdfiumGetSignatureCountResultP__Output | undefined
				) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						if (value?.exception) {
							handleRemoteException(value.exception, reject);
						}
						resolve(value.result == undefined ? 0 : value.result);
					}
				}
			);
		}
	);
}
