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 createProof(quoteKey: string) {
	const availableJob = await prisma.job.findUnique({
		where: { quoteKey },
		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);
	doc.text('Please check all details contained within this proof, including the fine details in the artwork. We cannot proceed with the job without written approval.', { align: 'right' });

	// 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);
			dataRows.push(row);
		}

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

		// 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(35);
		}
		columnStyles.push(50);

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

	// addNewPage(doc);
	doc.table({
		rowStyles: { border: false, backgroundColor: '#eeefef' },
		columnStyles: [250, '*'],
		data: [
			[
				{
					text: 'Remember to check',
					rowSpan: 5,
					align: { x: 'center', y: 'center' },
					font: {
						size: 12,
					},
				},
				{
					text: '1. Check artwork details',
					padding: [10, 2, 2, 2],
				},
			],
			[
				{
					text: '2. Check colours specifications',
				},
			],
			[
				{
					text: '3. Check artwork placement',
				},
			],
			[
				{
					text: '4. Check artwork size',
				},
			],
			[
				{
					text: '5. Check any spelling on artwork',
					padding: [2, 2, 10, 2],
				},
			],
		],
		position: { x: doc.x, y: doc.y },
	});
	doc.moveDown(2);
	doc.fontSize(8);

	// terms and conditions
	doc.text('');
	doc.table({
		rowStyles: { border: false },
		columnStyles: ['*'],
		data: [
			[
				{
					text: 'We will make every attempt to create error free artwork, but we require all proofs to be thoroughly checked by our clients. This includes colours, layout, sizes, spelling and overall accuracy of artwork and/or design. Please review this proof carefully and notify us immediately of any required changes. Short Batch is NOT responsible for errors or omissions after the proof has been approved and the job has been printed. Unless a specific PMS colour is requested by the customer, stock house colours may be substituted to reduce costs. Colours will be matched as close as possible to the proof but colours can appear differently on the final product than how they might appear on the digital layout proof. We will make every attempt to manufacture your product as close to the proof as possible. Standard variation for artwork placement on any garment will exist. All production runs have a 5% allowable defect rate. Any defects beyond this allowance will be reproduced or credited. No refunds or exchanges are possible due to the custom nature of our business. Garment defects such as rips, stains, tears, holes etc are responsibility of the manufacturer and are generally outside our control.',
				},
			],
		],
	});

	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;
		// put at bottom of page
		doc.fontSize(8).text('Please approve or reject this proof within 7 days of receipt.', 0.5 * (doc.page.width - 400), doc.page.height - 50, {
			align: 'center',
			width: 400,
		});
		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();
}
