import CreditNote from '#models/credit_note'
import GroupInvoice from '#models/group_invoice'
import {
  createCreditNoteByAdminValidator,
  editCreditNoteByAdminValidator,
  listCreditNotesByAdminValidator,
} from '#validators/credit_note'
import { Exception } from '@adonisjs/core/exceptions'
import type { HttpContext } from '@adonisjs/core/http'
import db from '@adonisjs/lucid/services/db'
import { DateTime } from 'luxon'
import { Readable } from 'stream'
import puppeteerBrowser from '#start/puppeteer'
import { CURRENCY } from '../../const/constants.js'
import { toBahrainCurrencyWords } from '../Helper/numberToCurrencyWordHelper.js'

export default class CreditNotesController {
  async createCreditNoteByAdmin({ request, response }: HttpContext) {
    const payload = await request.validateUsing(createCreditNoteByAdminValidator)

    try {
      await db.transaction(async (trx) => {
        const ifExistingCreditNote = await CreditNote.query({ client: trx })
          .where('invoice_id', payload.invoice_id)
          .first()

        if (ifExistingCreditNote) {
          throw new Exception('Credit note already exists for this invoice')
        }

        const groupInvoiceDetails = await GroupInvoice.query({ client: trx })
          .where('id', payload.invoice_id)
          .where('is_completely_paid', false)
          .preload('customer_details')
          .firstOrFail()

        if (groupInvoiceDetails.payable_amount < payload.amount) {
          throw new Exception('Credit note amount cannot be greater than invoice payable amount')
        }

        const lastCreditNote = await CreditNote.query({ client: trx }).orderBy('id', 'desc').first()

        let generated_credit_note_id = ''

        if (lastCreditNote) {
          const lastGeneratedId = lastCreditNote.generated_credit_note_id
          const idParts = lastGeneratedId.split('/')
          const newId = parseInt(idParts[1]) + 1
          generated_credit_note_id = `CN/${String(newId).padStart(3, '0')}/${DateTime.now().year}`
        } else {
          generated_credit_note_id = `CN/001/${DateTime.now().year}`
        }

        const creditNote = await CreditNote.create(
          {
            generated_credit_note_id,
            customer_id: groupInvoiceDetails.customer_id,
            invoice_type: groupInvoiceDetails.group_invoice_type,
            invoice_id: groupInvoiceDetails.id,
            credit_note_created_at: DateTime.now(),
            amount: payload.amount,
            notes: payload.notes,
          },
          { client: trx }
        )

        await creditNote.useTransaction(trx).refresh()

        return response.created({
          status: true,
          message: 'Credit note created successfully',
          data: creditNote,
        })
      })
    } catch (error) {
      if (error instanceof Exception) {
        return response.badRequest({
          status: false,
          message: error.message,
        })
      }

      return response.internalServerError({
        status: false,
        message: 'Failed to create credit note',
      })
    }
  }

  async listCreditNotesByAdmin({ request, response }: HttpContext) {
    const {
      page = 1,
      limit = 10,
      search,
      filter_by_customer_id,
      filter_by_invoice_type,
      filter_by_created_start_date,
      filter_by_created_end_date,
      sort_by_created_at,
      sort_by_updated_at,
      sort_by_amount,
    } = await request.validateUsing(listCreditNotesByAdminValidator)

    const creditNotes = await CreditNote.query()
      .if(search, (query) => {
        query.where((subQuery) => {
          subQuery
            .where('generated_credit_note_id', 'like', `%${search}%`)
            .orWhereHas('customer_details', (customerQuery) => {
              customerQuery.where('company_name', 'like', `%${search}%`)
              customerQuery.orWhere('first_name', 'like', `%${search}%`)
              customerQuery.orWhere('last_name', 'like', `%${search}%`)
            })
        })
      })
      .if(filter_by_customer_id?.length, (query) => {
        query.whereIn('customer_id', filter_by_customer_id as number[])
      })
      .if(filter_by_invoice_type?.length, (query) => {
        query.whereIn('invoice_type', filter_by_invoice_type as number[])
      })
      .if(filter_by_created_start_date, (query) => {
        query.where('created_at', '>=', filter_by_created_start_date as Date)
      })
      .if(filter_by_created_end_date, (query) => {
        query.where('created_at', '<=', filter_by_created_end_date as Date)
      })
      .if(sort_by_created_at, (query) => {
        query.orderBy('created_at', sort_by_created_at)
      })
      .if(sort_by_updated_at, (query) => {
        query.orderBy('updated_at', sort_by_updated_at)
      })
      .if(sort_by_amount, (query) => {
        query.orderBy('amount', sort_by_amount)
      })
      .if(!sort_by_created_at && !sort_by_updated_at && !sort_by_amount, (query) => {
        query.orderBy('created_at', 'desc')
      })
      .preload('customer_details', (customerQuery) => {
        customerQuery.select(
          'id',
          'short_code',
          'generated_id',
          'company_name',
          'company_registration_number',
          'first_name',
          'last_name'
        )
      })
      .paginate(page, limit)

    return response.ok({
      status: true,
      message: 'Credit notes fetched successfully',
      data: creditNotes,
    })
  }

  async downloadCreditNoteByAdmin({ params, response, view }: HttpContext) {
    const creditNoteId = params.id as number

    const creditNoteDetails = await CreditNote.query()
      .where('id', creditNoteId)
      .preload('invoice_details')
      .preload('customer_details')
      .firstOrFail()

    const data = {
      creditNoteNumber: creditNoteDetails.generated_credit_note_id,
      creditNoteCreationDate: creditNoteDetails.credit_note_created_at.toFormat('dd LLL yyyy'),
      currency: CURRENCY,
      creditAmount: creditNoteDetails.amount,
      customerName:
        creditNoteDetails.customer_details.company_name ||
        `${creditNoteDetails.customer_details.first_name} ${creditNoteDetails.customer_details.last_name}`,
      customerSingleLineAddressLineOne:
        creditNoteDetails.customer_details?.billing_address ??
        creditNoteDetails.customer_details?.address_line_one,
      customerSingleLineAddressLineTwo: creditNoteDetails.customer_details?.address_line_two,
      customerAddressLineOne: creditNoteDetails.customer_details.block_details?.id
        ? `${[creditNoteDetails.customer_details?.block_details?.code, creditNoteDetails.customer_details?.block_details?.name].join('-')}` ||
          ''
        : `${[creditNoteDetails.customer_details?.addresses?.[0]?.block?.code, creditNoteDetails.customer_details?.addresses?.[0]?.block?.name].join('-')}` ||
          '',
      customerAddressLineTwo:
        creditNoteDetails.customer_details.road_details?.id &&
        creditNoteDetails.customer_details.building_details?.id
          ? `${[creditNoteDetails.customer_details?.road_details?.code, creditNoteDetails.customer_details?.road_details?.name].join('-') || ''}, ${[creditNoteDetails.customer_details?.building_details?.code, creditNoteDetails.customer_details?.building_details?.name].join('-') || ''}`
          : `${[creditNoteDetails.customer_details?.addresses?.[0]?.road?.code, creditNoteDetails.customer_details?.addresses?.[0]?.road?.name].join('-') || ''}, ${[creditNoteDetails.customer_details?.addresses?.[0]?.building?.code, creditNoteDetails.customer_details?.addresses?.[0]?.building?.name].join('-') || ''}`,
      billingAddress: creditNoteDetails?.customer_details?.billing_address ?? '',
      customerPhoneNumber: creditNoteDetails.customer_details.phone || '',
      customerEmail: creditNoteDetails.customer_details.email || '',
      customerAccountNumber: creditNoteDetails.customer_details?.generated_id || '',
      customerVatNumber: creditNoteDetails.customer_details.vat_number || '',
      referenceInvoiceNumber: creditNoteDetails.invoice_details.generated_invoice_id,
      creditAmountInWords: toBahrainCurrencyWords(creditNoteDetails.amount),
    }
    // return data

    const page = await puppeteerBrowser.openNewPage()

    if (!page) {
      return response.internalServerError({
        status: false,
        message: 'Failed to puppeteer browser page',
      })
    }

    const html = await view.render('pages/pdf/creditNote', data)

    // return html

    await page.setContent(html)

    const pdf = await page.pdf({
      omitBackground: true,
      printBackground: true,
      timeout: 0,
      waitForFonts: true,
      // margin: { top: '20px', right: '20px', bottom: '20px', left: '20px' },
      // width: '120mm',
      // height: '170mm',
      format: 'A4',
    })

    await page.close()

    const pdfStream = new Readable()
    pdfStream.push(pdf)
    pdfStream.push(null)

    response.header('Content-type', 'application/pdf')
    response.header('Content-Disposition', `attachment; filename=${Date.now()}.pdf`)
    return response.stream(pdfStream)
  }

  async editCreditNoteByAdmin({ request, response }: HttpContext) {
    const {
      credit_note_id,
      amount,
      notes = null,
    } = await request.validateUsing(editCreditNoteByAdminValidator)

    try {
      await db.transaction(async (trx) => {
        const creditNoteDetails = await CreditNote.query({ client: trx })
          .where('id', credit_note_id)
          .firstOrFail()

        const groupInvoiceDetails = await GroupInvoice.query({ client: trx })
          .where('id', creditNoteDetails.invoice_id)
          .where('is_completely_paid', false)
          .preload('customer_details')
          .firstOrFail()

        if (groupInvoiceDetails.payable_amount < amount) {
          throw new Exception('Credit note amount cannot be greater than invoice payable amount')
        }

        creditNoteDetails.amount = amount
        creditNoteDetails.notes = notes

        await creditNoteDetails.useTransaction(trx).save()

        return response.ok({
          status: true,
          message: 'Credit note updated successfully',
          data: creditNoteDetails,
        })
      })
    } catch (error) {
      if (error instanceof Exception) {
        return response.badRequest({
          status: false,
          message: error.message,
        })
      }

      return response.internalServerError({
        status: false,
        message: 'Failed to update credit note',
      })
    }
  }

  async deleteCreditNoteByAdmin({ params, response }: HttpContext) {
    const creditNoteId = params.id as number

    try {
      const creditNoteDetails = await CreditNote.query().where('id', creditNoteId).firstOrFail()

      await creditNoteDetails.delete()

      return response.ok({
        status: true,
        message: 'Credit note deleted successfully',
      })
    } catch (error) {
      if (error instanceof Exception) {
        return response.badRequest({
          status: false,
        })
      }

      return response.internalServerError({
        status: false,
        message: 'Failed to delete credit note',
      })
    }
  }
}
