import { Injectable, Logger } from '@nestjs/common';
import { CreateQuoteDto } from './dto/create-quote.dto';
import { UpdateQuoteDto } from './dto/update-quote.dto';
import { PrismaService } from 'src/prisma/prisma.service';
import { createXeroInvoiceFromQuote } from 'xero/xeroHandler';
import { calculateTotal } from 'utils/calculateTotal';
import { createProof } from 'utils/pdf/artworkProof';
import { EmailsService } from 'src/emails/emails.service';

@Injectable()
export class QuotesService {
	constructor(
		private prisma: PrismaService,
		private emailer: EmailsService
	) {}
	private readonly logger = new Logger(QuotesService.name);

	create(createQuoteDto: CreateQuoteDto) {
		return 'This action adds a new quote';
	}

	regenQuoteKey(id: number) {
		const transaction = this.prisma.$transaction(async (tx) => {
			const quote = await tx.job.findUnique({
				where: { id },
			});
			if (!quote) {
				throw new Error('Quote not found');
			}
			const quoteKey = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
			const updatedQuote = await tx.job.update({
				where: { id },
				data: {
					quoteKey,
				},
			});
			return updatedQuote;
		});
		return transaction;
	}

	findAll(skip = 0, take = 10, sortBy: string, sortOrder: string, search: string = '', status: string | undefined = undefined) {
		const transaction = this.prisma.$transaction(async (tx) => {
			// this.logger.log({ level: 'info', message: 'Returning all jobs', refCode: '21202' });

			if (take < 0) {
				take = 100;
			}
			let searchID = parseInt(search);

			if (Number.isNaN(searchID)) {
				searchID = 0;
			}

			// Default Query
			let query = {
				skip,
				take,
				orderBy: {
					[sortBy]: sortOrder,
				},
				include: {
					client: true,
					milestones: true,
					createdBy: true,
				},
				where: {},
			} as any;

			// Add Search Query to Where
			if (search) {
				query.where.OR = [{ id: { equals: searchID } }, { title: { contains: search } }, { quoteKey: { contains: search } }, { invoiceNumber: { contains: search } }, { client: { name: { contains: search } } }];

				// Add Status to Where.OR
				// if (!status) query.where.OR.push({ status: { contains: search } });
			}

			// Add Status Query to Where
			if (status) {
				query.where.AND = [{ status: { contains: status } }];
			} else {
				query.where.AND = [{ status: { in: ['Quote', 'Rejected'] } }];
			}

			// Query
			var jobs = await tx.job.findManyAndCount(query);

			var customJobList = [];
			for (const job of jobs[0]) {
				var newJob: any = { ...job };

				newJob.totalItems = 0;
				newJob.totalAmount = 0;
				// @ts-ignore
				await calculateTotal(job.id, false, true).then((total) => {
					// @ts-ignore
					// this.logger.log({ level: 'info', message: `Total for job ${job.id} is ${JSON.stringify(total)}`, refCode: '21202' });
					if (total && total instanceof Object && !(total instanceof Array)) {
						newJob.totalItems = total.totalItems;
						newJob.totalAmount = total.totalAmount;
					}
				});

				customJobList.push(newJob);
			}
			jobs[0] = customJobList;
			return jobs;
		});
		return transaction;
	}

	findOne(quoteKey: string) {
		const transaction = this.prisma.$transaction(async (tx) => {
			const availableJob = await tx.job.findUnique({
				where: { quoteKey },
				include: {
					client: true,
					contacts: {
						select: {
							id: true,
							name: true,
							email: true,
							role: true,
						},
					},
					fees: {
						include: {
							fee: true,
						},
					},
				},
			});
			// this.logger.log(`Retrieved job with key: ${quoteKey}`);

			if (!availableJob) {
				throw new Error('Job not found with key: ' + quoteKey);
			}
			var combinedTotal = await calculateTotal(availableJob.id, false, true);
			// @ts-expect-error
			var newJob = { ...availableJob, ...combinedTotal };
			return newJob;
		});
		return transaction;
	}

	generateProof(quoteKey: string) {
		const transaction = this.prisma.$transaction(async (tx) => {
			const quote = await tx.job.findUnique({
				where: { quoteKey },
			});

			if (!quote) {
				throw new Error('Quote not found');
			}

			return createProof(quoteKey);
			// return quote;
		});
		return transaction;
	}

	async approve(key: string) {
		const transaction = await this.prisma.$transaction(async (tx) => {
			const update = await tx.job.update({
				where: { quoteKey: key },
				data: {
					status: 'Approved',
				},
			});
			this.logger.log(`Job with id: ${key} has been approved`);
			createXeroInvoiceFromQuote(key);
			return update;
		});

		return transaction;
	}

	emailQuote(key: string, email: string, subject?: string, message?: string, htmlMessage?: string) {
		const transaction = this.prisma.$transaction(async (tx) => {
			const quote = await tx.job.findUnique({
				where: { quoteKey: key },
			});

			if (!quote) {
				throw new Error('Quote not found');
			}

			await this.emailer.sendEmail(email, subject || `Quote for - ${quote.title}`, message || `Please find the quote for ${quote.title} attached.`, htmlMessage || `<p>Please find the quote for <strong>${quote.title}</strong> attached.</p>`);

			this.logger.log(`Quote with key: ${key} has been emailed to ${email}`);
			return { message: `Quote emailed to ${email}` };
		});
		return transaction;
	}

	async reject(key: string, shippingNotes: string) {
		const transaction = await this.prisma.$transaction(async (tx) => {
			const update = await tx.job.update({
				where: { quoteKey: key },
				data: {
					status: 'Rejected',
					shippingNotes: shippingNotes,
				},
			});
			this.logger.log(`Job with id: ${key} has been rejected`);
			createProof(key);
			return update;
		});
		return transaction;
	}

	update(id: number, updateQuoteDto: UpdateQuoteDto) {
		return `This action updates a #${id} quote`;
	}

	remove(id: number) {
		return `This action removes a #${id} quote`;
	}
}
