import * as grpc from "@grpc/grpc-js";
import { ServiceError } from "@grpc/grpc-js";

import fs from "fs";
import {
	ChromePdfRenderOptions,
	HttpLoginCredentials,
} from "../../../public/render";
import { IronPdfServiceClient } from "../../generated_proto/ironpdfengineproto/IronPdfService";
import { Access } from "../../access";
import { PdfDocumentResultP__Output } from "../../generated_proto/ironpdfengineproto/PdfDocumentResultP";
import {
	chunkBuffer,
	chunkString,
	handlePdfDocumentResultP__Output,
} from "../util";
import {
	chromePdfRenderOptionsToProto,
	httpLoginCredentialsToProto,
} from "./converter";
import { ChromeRenderPdfDocumentFromUriRequestP } from "../../generated_proto/ironpdfengineproto/ChromeRenderPdfDocumentFromUriRequestP";
import {
	_ironpdfengineproto_ChromeRenderPdfDocumentFromHtmlRequestStreamP_InfoP,
	ChromeRenderPdfDocumentFromHtmlRequestStreamP,
} from "../../generated_proto/ironpdfengineproto/ChromeRenderPdfDocumentFromHtmlRequestStreamP";
import { PdfDocumentP } from "../../generated_proto/ironpdfengineproto/PdfDocumentP";

export async function renderHtmlZipToPdf(
	zipFilePath: string,
	mainHtmlFile = "Basic.html",
	renderOption?: ChromePdfRenderOptions | undefined,
	httpLoginCredentials?: HttpLoginCredentials | undefined
): Promise<string> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise(
		(resolve: (_: string) => void, reject: (errorMsg: string) => void) => {
			const stream = client.chromeRenderFromZipFile(
				(
					err: ServiceError | null,
					value: PdfDocumentResultP__Output | undefined
				) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handlePdfDocumentResultP__Output(
							value,
							resolve,
							reject
						);
					}
				}
			);

			stream.write({
				info: {
					renderOptions: chromePdfRenderOptionsToProto(renderOption),
					httpOptions:
						httpLoginCredentialsToProto(httpLoginCredentials),
					mainFile: mainHtmlFile,
				},
			});

			const buf = fs.readFileSync(zipFilePath);

			if (buf) {
				chunkBuffer(buf).forEach((chunk) => {
					stream.write({ zipChunk: chunk });
				});
			}
			stream.end();
		}
	);
}

export async function renderUrlToPdf(
	url: URL,
	options?: {
		renderOptions?: ChromePdfRenderOptions | undefined;
		httpLoginCredentials?: HttpLoginCredentials | undefined;
	} | undefined
): Promise<string> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise(
		(resolve: (_: string) => void, reject: (errorMsg: string) => void) => {
			const req: ChromeRenderPdfDocumentFromUriRequestP = {
				uri: url.toString(),
				renderOptions: chromePdfRenderOptionsToProto(
					options?.renderOptions
				),
				httpOptions: httpLoginCredentialsToProto(
					options?.httpLoginCredentials
				),
			};

			client.Chrome_Render_FromUri(
				req,
				(
					err: ServiceError | null,
					value: PdfDocumentResultP__Output | undefined
				) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handlePdfDocumentResultP__Output(
							value,
							resolve,
							reject
						);
					}
				}
			);
		}
	);
}

export async function renderHtmlToPdf(
	html: string,
	renderOption?: ChromePdfRenderOptions | undefined,
	httpLoginCredentials?: HttpLoginCredentials | undefined
): Promise<string> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise(
		(resolve: (_: string) => void, reject: (errorMsg: string) => void) => {
			const stream: grpc.ClientWritableStream<ChromeRenderPdfDocumentFromHtmlRequestStreamP> =
				client.Chrome_Render_FromHtml(
					(
						err: ServiceError | null,
						value: PdfDocumentResultP__Output | undefined
					) => {
						if (err) {
							reject(`${err.name}/n${err.message}`);
						} else if (value) {
							handlePdfDocumentResultP__Output(
								value,
								resolve,
								reject
							);
						}
					}
				);

			const info: _ironpdfengineproto_ChromeRenderPdfDocumentFromHtmlRequestStreamP_InfoP =
				{
					renderOptions: chromePdfRenderOptionsToProto(renderOption),
					httpOptions:
						httpLoginCredentialsToProto(httpLoginCredentials),
				};
			stream.write({ info: info });

			const chunks = chunkString(html);
			chunks?.forEach((chunk) => {
				stream.write({ htmlChunk: chunk });
			});
			stream.end();
		}
	);
}

export async function mergePdfs(pdfIds: string[]): Promise<string> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise(
		(resolve: (_: string) => void, reject: (errorMsg: string) => void) => {
			client.pdfiumPageMerge(
				{
					documents: pdfIds.map<PdfDocumentP>(
						(value) => <PdfDocumentP>{ documentId: value }
					),
				},
				(
					err: ServiceError | null,
					value: PdfDocumentResultP__Output | undefined
				) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handlePdfDocumentResultP__Output(
							value,
							resolve,
							reject
						);
					}
				}
			);
		}
	);
}
