import { AuthMethod, MultiTenancyInterceptor, UseAuth } from '@backend/auth'
import { TableRepository, TableTokenRepository } from '@backend/repository'
import { removeNulls } from '@core/utils'
import { Controller, ForbiddenException, Get, Logger, Param, Res, UseInterceptors } from '@nestjs/common'
import { ApiForbiddenResponse, ApiInternalServerErrorResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'
import { FastifyReply } from 'fastify'
import PDFDocument from 'pdfkit'
import QRCode from 'qrcode'
import { In } from 'typeorm'
import { CUSTOMER_APP_URL } from '../../../config'
import { TableTokenResponse } from '../types'

@Controller('customer/token')
@ApiTags('customer')
export class TokenController {
	private readonly logger = new Logger(TokenController.name)

	constructor(
		private readonly _tableTokenRepository: TableTokenRepository,
		private readonly _tableRepository: TableRepository,
	) {}

	@Get('verify/:token')
	@ApiOkResponse({ type: TableTokenResponse })
	@ApiForbiddenResponse()
	@ApiInternalServerErrorResponse()
	async verify(@Param('token') token: string): Promise<TableTokenResponse> {
		const foundToken = await this._tableTokenRepository.findOne({ where: { token } })
		if (foundToken === null || !foundToken.tenantId) {
			this.logger.warn(`Invalid table token provided ${token}`)
			throw new ForbiddenException('Token is invalid')
		}
		return { tableId: foundToken.tableId, tenantId: foundToken.tenantId }
	}

	@UseAuth(AuthMethod.JWT)
	@UseInterceptors(MultiTenancyInterceptor)
	@Get('qrcode')
	@ApiOkResponse()
	@ApiInternalServerErrorResponse()
	async generateQrCodes(@Res() reply: FastifyReply): Promise<void> {
		const tables = await this._tableRepository.find()
		const tokens = await this._tableTokenRepository.find({ where: { tableId: In(tables.map((table) => table.id)) } })

		const tableTokens = tables
			.map((table) => {
				const token = tokens.find(({ tableId }) => tableId === table.id)
				if (token === undefined) return

				return { table, token: token.token }
			})
			.filter(removeNulls)

		// eslint-disable-next-line no-async-promise-executor
		const pdfBuffer: Buffer = await new Promise(async (resolve) => {
			const doc = new PDFDocument({
				size: 'LETTER',
				bufferPages: true,
			})

			doc.fontSize(24).text('QRCodes', { align: 'center' })

			for (const { table, token } of tableTokens) {
				doc.fontSize(20).text(table.name, { align: 'center' })

				const url = `${CUSTOMER_APP_URL}/token?token=${token}`
				const imageBuffer = await QRCode.toBuffer(url)
				doc.image(imageBuffer, { fit: [300, 300], align: 'center' })

				doc.addPage()
			}

			doc.end()

			const buffer: Uint8Array[] = []
			doc.on('data', buffer.push.bind(buffer))
			doc.on('end', () => {
				const data = Buffer.concat(buffer)
				resolve(data)
			})
		})

		reply.headers({
			'Content-Type': 'application/pdf',
			'Content-Disposition': 'attachment; filename=example.pdf',
			'Content-Length': pdfBuffer.length,
		})

		reply.status(200).send(pdfBuffer)
	}
}
