import PDFDocument, { addPage, font, text } from 'pdfkit';
import fs from 'fs';

import { PrismaService } from 'nestjs-prisma';
import { size } from 'pdfkit/js/page';
const prisma = new PrismaService();

export async function createStockTake(jobid: number) {
	const availableJob = await prisma.job.findUnique({
		where: { id: jobid },
		include: {
			client: true,
		},
	});
	const url = await generatePDF(availableJob);
	let returnUrl = 'http://localhost:3001/public/pdfs/' + url;

	const returnJson = {
		url: returnUrl,
		jobId: availableJob?.id,
	};
	return returnJson;
}

async function generatePDF(availableJob: any) {
	if (!availableJob) {
		throw new Error('Job not found');
	}

	const designs = await prisma.design.findMany({
		where: {
			jobId: availableJob.id,
		},
		include: {
			imprints: {
				include: {
					inks: {
						include: {
							ink: true,
						},
					},
					images: true,
				},
			},
			lineItems: {
				include: {
					product: true,
					sizes: true,
				},
			},
		},
	});

	// PDF Generation

	const doc = new PDFDocument({ autoFirstPage: false, bufferPages: true });
	const date = new Date().toISOString().split('T')[0];
	const url = availableJob.id + '_artwork_proof_' + date + '.pdf';
	const stream = fs.createWriteStream('public/pdfs/' + url);

	doc.pipe(stream);

	doc.registerFont('Roboto', 'public/fonts/Roboto-Regular.ttf');

	addNewPage(doc);
	doc.font('Roboto');

	// reset x position
	doc.x = 50;
	// reset y position
	doc.y = 50;
	doc.image('public/logo.png', {
		fit: [100, 100],
	});

	// reset y position
	doc.y = 50;

	doc.fontSize(20).text(availableJob.title + ' #' + availableJob.id, {
		align: 'right',
	});

	// job details and shipping details side by side
	doc.fontSize(8);

	doc.moveDown();

	const currentDateTime = new Date();
	// format as HH:MM AM/PM DD/MM/YYYY
	const formattedDateTime = `${currentDateTime.getHours() % 12}:${currentDateTime.getMinutes().toString().padStart(2, '0')} ${currentDateTime.getHours() < 12 ? 'AM' : 'PM'}\n${currentDateTime.getDate().toString().padStart(2, '0')}/${(currentDateTime.getMonth() + 1).toString().padStart(2, '0')}/${currentDateTime.getFullYear()}`;

	const invoiceDate = availableJob.invoiceDate ? new Date(availableJob.invoiceDate) : null;
	const formattedInvoiceDate = invoiceDate ? `${invoiceDate.getDate().toString().padStart(2, '0')}/${(invoiceDate.getMonth() + 1).toString().padStart(2, '0')}/${invoiceDate.getFullYear()}` : 'N/A';

	doc.table({
		// add padding to every outer cell in first and last column
		rowStyles: { border: false, backgroundColor: '#eeefef' },
		columnStyles: [75, 100, 150],
		data: [
			[
				{
					text: 'Client:',

					textStroke: 0.5,
					padding: [10, 2, 2, 10],
				},
				{
					text: availableJob.client.name,
					padding: [10, 2, 2, 2],
				},
				{
					text: 'Ship To:',
					font: {
						family: 'Helvetica',
					},
					textStroke: 0.5,
					padding: [10, 10, 2, 2],
				},
			],
			[
				{
					text: 'Due Date:',
					textStroke: 0.5,
					padding: [2, 2, 2, 10],
				},
				{
					text: formattedInvoiceDate,
					padding: [2, 2, 2, 2],
				},
				{
					text: `${availableJob.shippingName}\n${availableJob.shippingAddress1}\n${availableJob.shippingAddress2}\n${availableJob.shippingCity}, ${availableJob.shippingState} ${availableJob.shippingPostcode}\n`,
					colSpan: 1,
					rowSpan: 2,
					padding: [2, 10, 10, 2],
				},
			],
			[
				{
					text: 'Created At:',
					textStroke: 0.5,
					padding: [2, 2, 10, 10],
				},
				{
					text: formattedDateTime,
					padding: [2, 2, 10, 2],
				},
			],
		],

		position: { x: doc.page.width - 400, y: doc.y },
	});
	doc.moveDown(2);

	// reset x position
	doc.x = 50;
	doc.moveDown(2);

	for (const design of designs) {
		doc.fontSize(20).text(design.name, {
			// align: 'center',
		});
		doc.fontSize(8);
		doc.moveDown();

		var allSizesSet = new Set<string>();
		var dataRows: any[] = [];

		// get all unique sizes from line items
		for (const lineItem of design.lineItems) {
			for (const size of lineItem.sizes) {
				allSizesSet.add(size.name);
			}
		}

		// loop through line items to create table rows
		for (const lineItem of design.lineItems) {
			var row: any[] = [];
			row.push(lineItem.product.title);
			allSizesSet.forEach((sizeName) => {
				const size = lineItem.sizes.find((s) => s.name === sizeName);
				row.push(size ? size.amount : 0);
			});

			row.push(lineItem.total);
			row.push('');
			row.push('');
			row.push('');
			dataRows.push(row);
		}

		const header = [{ text: 'ITEM', backgroundColor: '#231f20', textColor: '#ffffff' }, ...Array.from(allSizesSet), 'TOTAL', { text: ' received', textOptions: { rotation: 90 } }, { text: ' printed', textOptions: { rotation: 90 } }, { text: ' packed', textOptions: { rotation: 90 } }];

		// create dynamic row styles
		// size columns are max 50 width, item column is 200 width, total column is 50 width
		const columnStyles: any[] = [];
		columnStyles.push('*');
		for (let i = 0; i < allSizesSet.size; i++) {
			columnStyles.push(30);
		}
		columnStyles.push(35);
		columnStyles.push(20);
		columnStyles.push(20);
		columnStyles.push(20);

		doc.table({
			defaultStyle: {
				align: { x: 'center', y: 'center' },
			},
			columnStyles: (i) => {
				if (i === 0) {
					return { width: '*', align: { x: 'left', y: 'center' }, padding: [6, 0, 2, 10] };
				}
				return { width: columnStyles[i], align: { x: 'center', y: 'center' }, padding: [6, 0, 2, 0] };
			},
			rowStyles: (i) => {
				if (i === 0) {
					return {
						font: 'Helvetica-Bold',
						backgroundColor: '#eeefef',
					};
				}
			},

			data: [header, ...dataRows],
			position: { x: doc.x, y: doc.y },
		});

		// imprint details
		// indent by 20
		doc.x += 20;

		for (const imprint of design.imprints) {
			// imprint info table
			doc.moveDown();

			doc.table({
				rowStyles: { border: false, backgroundColor: '#eeefef' },
				columnStyles: [100, '*'],
				data: [
					[
						{
							text: 'Imprint' + ' #' + imprint.id,
							rowSpan: 5,
							align: { x: 'center', y: 'center' },
						},
						{
							text: 'Type: ' + imprint.type,
							padding: [10, 2, 2, 2],
						},
					],
					[
						{
							text: 'Inks:' + imprint.inks.map((i: any) => i.ink.name).join(', '),
						},
					],
					[
						{
							text: 'Location: ' + imprint.location,
						},
					],
					[
						{
							text: 'Size (mm): ' + imprint.height + 'H x ' + imprint.width + 'W',
						},
					],
					[
						{
							text: 'Mesh: ' + imprint.inks.map((i: any) => i.ink.mesh).join(', '),
							padding: [2, 2, 10, 2],
						},
					],
				],
				position: { x: doc.x, y: doc.y },
			});
			doc.moveDown();
			// imprint images 3 col layout
			const imagesPerRow = 3;
			var currentY = doc.y;
			if (imprint.images.length > 0) {
				const workingWidth = (doc.page.width - doc.x - 50) / 3;
				const workingHeight = 150;

				for (let i = 0; i < imprint.images.length; i++) {
					const image = imprint.images[i];

					// working width
					// if currentY + workingHeight exceeds page height, add new page
					if (currentY + workingHeight > doc.page.height - doc.page.margins.bottom) {
						addNewPage(doc);
						currentY = doc.y;
					}

					const x = doc.x + (i % imagesPerRow) * workingWidth;
					const y = currentY;
					doc.image('public/images/' + image.url, x, y, { fit: [workingWidth - 10, workingHeight - 10] });

					// after placing 3 images, increase currentY
					if ((i + 1) % imagesPerRow === 0) {
						currentY += workingHeight;
					}
				}
				doc.y = currentY + workingHeight;
			}
		}

		doc.x = 50;
	}

	doc.fontSize(8);

	finishDoc(doc);

	await new Promise<void>((resolve) => {
		stream.on('finish', function () {
			resolve();
		});
	});

	return url;
}

function finishDoc(doc: PDFKit.PDFDocument) {
	const range = doc.bufferedPageRange(); // => { start: 0, count: 2 }
	for (let i = range.start; i < range.start + range.count; i++) {
		doc.switchToPage(i);
		// Header
		let bottom = doc.page.margins.bottom;
		doc.page.margins.bottom = 0;

		doc.text(`Page ${i + 1} of ${doc.bufferedPageRange().count}`, 0.5 * (doc.page.width - 100), doc.page.height - 40, {
			align: 'center',
			width: 100,
		});
		doc.page.margins.bottom = bottom;
	}

	doc.end();
}

function addNewPage(doc: PDFKit.PDFDocument) {
	doc.addPage();
}
