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 {
	_ironpdfengineproto_ChromeRenderPdfDocumentFromHtmlFileRequestStreamP_InfoP,
	ChromeRenderPdfDocumentFromHtmlFileRequestStreamP,
} from "../../generated_proto/ironpdfengineproto/ChromeRenderPdfDocumentFromHtmlFileRequestStreamP";

import { PdfDocumentP } from "../../generated_proto/ironpdfengineproto/PdfDocumentP";
import {execSync} from "child_process";

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 renderHtmlFileToPdf(
	htmlPath: string,
	renderOption?: ChromePdfRenderOptions | undefined,
	httpLoginCredentials?: HttpLoginCredentials | undefined
): Promise<string> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	function isIronPdfProcessRunning() {
		try {
			const cmd: string = process.platform === "win32" ? `tasklist` : `ps aux`;
			const lines = execSync(cmd, { encoding: "utf-8" }).split("\n");
			return lines.some(line => line.trim().startsWith("IronPdfEngineConsole"));
		} catch (err) {
			return false;
		}
	}
	if(!isIronPdfProcessRunning()) {
		const htmlString = fs.readFileSync(htmlPath).toString();
		return renderHtmlToPdf(htmlString, renderOption, httpLoginCredentials);
	}
	try {
		return new Promise(
			(resolve: (_: string) => void, reject: (errorMsg: string) => void) => {
				const stream: grpc.ClientWritableStream<ChromeRenderPdfDocumentFromHtmlFileRequestStreamP> =
					client.Chrome_Render_FromHtmlFile(
						(
							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_ChromeRenderPdfDocumentFromHtmlFileRequestStreamP_InfoP = {
					renderOptions: chromePdfRenderOptionsToProto(renderOption),
					httpOptions: httpLoginCredentialsToProto(httpLoginCredentials),
				};
				stream.write({ info: info });
				stream.write({htmlPath: htmlPath});
				stream.end();
			}
		);
	} catch (e) {
		const htmlString = fs.readFileSync(htmlPath).toString();
		return renderHtmlToPdf(htmlString, renderOption, httpLoginCredentials);
	}
}

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
						);
					}
				}
			);
		}
	);
}
