import type { FastifyPluginAsync } from 'fastify'
import { z } from 'zod'
import {
  PaginationSchema,
  TransactionCreateSchema,
  TransactionLineItemCreateSchema,
  ApplyDiscountSchema,
  CompleteTransactionSchema,
} from '@lunarfront/shared/schemas'
import { inArray } from 'drizzle-orm'
import { TransactionService } from '../../services/transaction.service.js'
import { appConfig } from '../../db/schema/stores.js'
import { EmailService } from '../../services/email.service.js'
import { renderReceiptEmailHtml, renderReceiptEmailText } from '../../utils/email-templates.js'

const FromRepairBodySchema = z.object({
  locationId: z.string().uuid().optional(),
})

export const transactionRoutes: FastifyPluginAsync = async (app) => {
  app.post('/transactions', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => {
    const parsed = TransactionCreateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const txn = await TransactionService.create(app.db, parsed.data, request.user.id)
    request.log.info({ transactionId: txn.id, type: parsed.data.transactionType, userId: request.user.id }, 'Transaction created')
    return reply.status(201).send(txn)
  })

  app.post('/transactions/from-repair/:ticketId', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => {
    const { ticketId } = request.params as { ticketId: string }
    const parsed = FromRepairBodySchema.safeParse(request.body ?? {})
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const txn = await TransactionService.createFromRepairTicket(app.db, ticketId, parsed.data.locationId, request.user.id)
    request.log.info({ transactionId: txn.id, ticketId, userId: request.user.id }, 'Repair payment transaction created')
    return reply.status(201).send(txn)
  })

  app.get('/transactions', { preHandler: [app.authenticate, app.requirePermission('pos.view')] }, async (request, reply) => {
    const query = request.query as Record<string, string | undefined>
    const params = PaginationSchema.parse(query)

    const filters = {
      status: query.status,
      transactionType: query.transactionType,
      locationId: query.locationId,
      accountId: query.accountId,
      itemSearch: query.itemSearch,
    }

    const result = await TransactionService.list(app.db, params, filters)
    return reply.send(result)
  })

  app.get('/transactions/:id', { preHandler: [app.authenticate, app.requirePermission('pos.view')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const txn = await TransactionService.getById(app.db, id)
    if (!txn) return reply.status(404).send({ error: { message: 'Transaction not found', statusCode: 404 } })
    return reply.send(txn)
  })

  app.get('/transactions/:id/receipt', { preHandler: [app.authenticate, app.requirePermission('pos.view')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const receipt = await TransactionService.getReceipt(app.db, id)
    return reply.send(receipt)
  })

  app.post('/transactions/:id/email-receipt', { preHandler: [app.authenticate, app.requirePermission('pos.view')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = z.object({ email: z.string().email() }).safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Valid email is required', statusCode: 400 } })
    }

    // Rate limit: max 5 emails per transaction per hour
    const rateKey = `email-receipt:${id}`
    const count = await app.redis.incr(rateKey)
    if (count === 1) await app.redis.expire(rateKey, 3600)
    if (count > 5) {
      return reply.status(429).send({ error: { message: 'Too many emails for this receipt. Try again later.', statusCode: 429 } })
    }

    const receipt = await TransactionService.getReceipt(app.db, id)
    const storeName = receipt.company?.name ?? 'Store'

    // Fetch receipt config
    const configRows = await app.db.select({ key: appConfig.key, value: appConfig.value })
      .from(appConfig)
      .where(inArray(appConfig.key, ['receipt_header', 'receipt_footer', 'receipt_return_policy', 'receipt_social']))
    const config: Record<string, string> = {}
    for (const row of configRows) if (row.value) config[row.key] = row.value

    const html = renderReceiptEmailHtml(receipt as any, config)
    const text = renderReceiptEmailText(receipt as any, config)

    await EmailService.send(app.db, {
      to: parsed.data.email,
      subject: `Your receipt from ${storeName} — ${receipt.transaction.transactionNumber}`,
      html,
      text,
    })

    request.log.info({ transactionId: id, email: parsed.data.email, userId: request.user.id }, 'Receipt email sent')
    return reply.send({ message: 'Receipt sent', sentTo: parsed.data.email })
  })

  app.post('/transactions/:id/line-items', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = TransactionLineItemCreateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const lineItem = await TransactionService.addLineItem(app.db, id, parsed.data)
    return reply.status(201).send(lineItem)
  })

  app.delete('/transactions/:id/line-items/:lineItemId', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => {
    const { id, lineItemId } = request.params as { id: string; lineItemId: string }
    const deleted = await TransactionService.removeLineItem(app.db, id, lineItemId)
    return reply.send(deleted)
  })

  app.post('/transactions/:id/discounts', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = ApplyDiscountSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    await TransactionService.applyDiscount(app.db, id, parsed.data, request.user.id)
    const txn = await TransactionService.getById(app.db, id)
    return reply.send(txn)
  })

  app.post('/transactions/:id/complete', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = CompleteTransactionSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    await TransactionService.complete(app.db, id, parsed.data)
    const txn = await TransactionService.getById(app.db, id)
    request.log.info({ transactionId: id, paymentMethod: parsed.data.paymentMethod, userId: request.user.id }, 'Transaction completed')
    return reply.send(txn)
  })

  app.post('/transactions/:id/void', { preHandler: [app.authenticate, app.requirePermission('pos.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    await TransactionService.void(app.db, id, request.user.id)
    const txn = await TransactionService.getById(app.db, id)
    request.log.info({ transactionId: id, voidedBy: request.user.id }, 'Transaction voided')
    return reply.send(txn)
  })
}
