import type { FastifyPluginAsync } from 'fastify'
import {
  AccountCreateSchema,
  AccountUpdateSchema,
  MemberCreateSchema,
  MemberUpdateSchema,
  PaginationSchema,
  ProcessorLinkCreateSchema,
  ProcessorLinkUpdateSchema,
  PaymentMethodCreateSchema,
  PaymentMethodUpdateSchema,
  TaxExemptionCreateSchema,
  TaxExemptionUpdateSchema,
  MemberIdentifierCreateSchema,
  MemberIdentifierUpdateSchema,
} from '@lunarfront/shared/schemas'
import {
  AccountService,
  MemberService,
  MemberIdentifierService,
  ProcessorLinkService,
  PaymentMethodService,
  TaxExemptionService,
} from '../../services/account.service.js'

export const accountRoutes: FastifyPluginAsync = async (app) => {
  // --- Accounts ---

  app.post('/accounts', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const parsed = AccountCreateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const account = await AccountService.create(app.db, parsed.data)
    return reply.status(201).send(account)
  })

  app.get('/accounts', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const params = PaginationSchema.parse(request.query)
    const result = await AccountService.list(app.db, params)
    return reply.send(result)
  })

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

  app.patch('/accounts/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = AccountUpdateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const account = await AccountService.update(app.db, id, parsed.data)
    if (!account) return reply.status(404).send({ error: { message: 'Account not found', statusCode: 404 } })
    return reply.send(account)
  })

  app.delete('/accounts/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const account = await AccountService.softDelete(app.db, id)
    if (!account) return reply.status(404).send({ error: { message: 'Account not found', statusCode: 404 } })
    request.log.info({ accountId: id, userId: request.user.id }, 'Account soft-deleted')
    return reply.send(account)
  })

  // --- Members (top-level) ---

  app.get('/members', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const params = PaginationSchema.parse(request.query)
    const result = await MemberService.list(app.db, params)
    return reply.send(result)
  })

  // --- Members (scoped to account) ---

  app.post('/accounts/:accountId/members', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { accountId } = request.params as { accountId: string }
    const parsed = MemberCreateSchema.safeParse({ ...(request.body as object), accountId })
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const member = await MemberService.create(app.db, parsed.data)
    return reply.status(201).send(member)
  })

  app.get('/accounts/:accountId/members', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const { accountId } = request.params as { accountId: string }
    const params = PaginationSchema.parse(request.query)
    const result = await MemberService.listByAccount(app.db, accountId, params)
    return reply.send(result)
  })

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

  app.patch('/members/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = MemberUpdateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const member = await MemberService.update(app.db, id, parsed.data)
    if (!member) return reply.status(404).send({ error: { message: 'Member not found', statusCode: 404 } })
    return reply.send(member)
  })

  app.post('/members/:id/move', { preHandler: [app.authenticate, app.requirePermission('accounts.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const { accountId } = (request.body as { accountId?: string }) ?? {}

    let targetAccountId = accountId

    // If no accountId provided, create a new account from the member's name
    if (!targetAccountId) {
      const member = await MemberService.getById(app.db, id)
      if (!member) return reply.status(404).send({ error: { message: 'Member not found', statusCode: 404 } })
      const account = await AccountService.create(app.db, {
        name: `${member.firstName} ${member.lastName}`,
        billingMode: 'consolidated',
      })
      targetAccountId = account.id
    }

    const member = await MemberService.move(app.db, id, targetAccountId)
    if (!member) return reply.status(404).send({ error: { message: 'Member not found', statusCode: 404 } })
    request.log.info({ memberId: id, targetAccountId, userId: request.user.id }, 'Member moved to account')
    return reply.send(member)
  })

  // --- Member Identifiers ---

  app.post('/members/:memberId/identifiers', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { memberId } = request.params as { memberId: string }
    const parsed = MemberIdentifierCreateSchema.safeParse({ ...(request.body as object), memberId })
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const identifier = await MemberIdentifierService.create(app.db, parsed.data)
    return reply.status(201).send(identifier)
  })

  app.get('/members/:memberId/identifiers', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const { memberId } = request.params as { memberId: string }
    const params = PaginationSchema.parse(request.query)
    const result = await MemberIdentifierService.listByMember(app.db, memberId, params)
    return reply.send(result)
  })

  app.patch('/identifiers/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = MemberIdentifierUpdateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const identifier = await MemberIdentifierService.update(app.db, id, parsed.data)
    if (!identifier) return reply.status(404).send({ error: { message: 'Identifier not found', statusCode: 404 } })
    return reply.send(identifier)
  })

  app.delete('/identifiers/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const identifier = await MemberIdentifierService.delete(app.db, id)
    if (!identifier) return reply.status(404).send({ error: { message: 'Identifier not found', statusCode: 404 } })
    return reply.send(identifier)
  })

  app.delete('/members/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const member = await MemberService.delete(app.db, id)
    if (!member) return reply.status(404).send({ error: { message: 'Member not found', statusCode: 404 } })
    return reply.send(member)
  })

  // --- Processor Links ---

  app.post('/accounts/:accountId/processor-links', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { accountId } = request.params as { accountId: string }
    const parsed = ProcessorLinkCreateSchema.safeParse({ ...(request.body as object), accountId })
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const link = await ProcessorLinkService.create(app.db, parsed.data)
    return reply.status(201).send(link)
  })

  app.get('/accounts/:accountId/processor-links', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const { accountId } = request.params as { accountId: string }
    const params = PaginationSchema.parse(request.query)
    const result = await ProcessorLinkService.listByAccount(app.db, accountId, params)
    return reply.send(result)
  })

  app.patch('/processor-links/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = ProcessorLinkUpdateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const link = await ProcessorLinkService.update(app.db, id, parsed.data)
    if (!link) return reply.status(404).send({ error: { message: 'Processor link not found', statusCode: 404 } })
    return reply.send(link)
  })

  app.delete('/processor-links/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const link = await ProcessorLinkService.delete(app.db, id)
    if (!link) return reply.status(404).send({ error: { message: 'Processor link not found', statusCode: 404 } })
    return reply.send(link)
  })

  // --- Payment Methods ---

  app.post('/accounts/:accountId/payment-methods', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { accountId } = request.params as { accountId: string }
    const parsed = PaymentMethodCreateSchema.safeParse({ ...(request.body as object), accountId })
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const method = await PaymentMethodService.create(app.db, parsed.data)
    return reply.status(201).send(method)
  })

  app.get('/accounts/:accountId/payment-methods', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const { accountId } = request.params as { accountId: string }
    const params = PaginationSchema.parse(request.query)
    const result = await PaymentMethodService.listByAccount(app.db, accountId, params)
    return reply.send(result)
  })

  app.get('/payment-methods/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const method = await PaymentMethodService.getById(app.db, id)
    if (!method) return reply.status(404).send({ error: { message: 'Payment method not found', statusCode: 404 } })
    return reply.send(method)
  })

  app.patch('/payment-methods/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = PaymentMethodUpdateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const method = await PaymentMethodService.update(app.db, id, parsed.data)
    if (!method) return reply.status(404).send({ error: { message: 'Payment method not found', statusCode: 404 } })
    return reply.send(method)
  })

  app.delete('/payment-methods/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const method = await PaymentMethodService.delete(app.db, id)
    if (!method) return reply.status(404).send({ error: { message: 'Payment method not found', statusCode: 404 } })
    return reply.send(method)
  })

  // --- Tax Exemptions ---

  app.post('/accounts/:accountId/tax-exemptions', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { accountId } = request.params as { accountId: string }
    const parsed = TaxExemptionCreateSchema.safeParse({ ...(request.body as object), accountId })
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const exemption = await TaxExemptionService.create(app.db, parsed.data)
    return reply.status(201).send(exemption)
  })

  app.get('/accounts/:accountId/tax-exemptions', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const { accountId } = request.params as { accountId: string }
    const params = PaginationSchema.parse(request.query)
    const result = await TaxExemptionService.listByAccount(app.db, accountId, params)
    return reply.send(result)
  })

  app.get('/tax-exemptions/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.view')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const exemption = await TaxExemptionService.getById(app.db, id)
    if (!exemption) return reply.status(404).send({ error: { message: 'Tax exemption not found', statusCode: 404 } })
    return reply.send(exemption)
  })

  app.patch('/tax-exemptions/:id', { preHandler: [app.authenticate, app.requirePermission('accounts.edit')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const parsed = TaxExemptionUpdateSchema.safeParse(request.body)
    if (!parsed.success) {
      return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
    }
    const exemption = await TaxExemptionService.update(app.db, id, parsed.data)
    if (!exemption) return reply.status(404).send({ error: { message: 'Tax exemption not found', statusCode: 404 } })
    return reply.send(exemption)
  })

  app.post('/tax-exemptions/:id/approve', { preHandler: [app.authenticate, app.requirePermission('accounts.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const exemption = await TaxExemptionService.approve(app.db, id, request.user.id)
    if (!exemption) return reply.status(404).send({ error: { message: 'Tax exemption not found', statusCode: 404 } })
    request.log.info({ exemptionId: id, accountId: exemption.accountId, userId: request.user.id }, 'Tax exemption approved')
    return reply.send(exemption)
  })

  app.post('/tax-exemptions/:id/revoke', { preHandler: [app.authenticate, app.requirePermission('accounts.admin')] }, async (request, reply) => {
    const { id } = request.params as { id: string }
    const { reason } = (request.body as { reason?: string }) ?? {}
    if (!reason) {
      return reply.status(400).send({ error: { message: 'Reason is required to revoke a tax exemption', statusCode: 400 } })
    }
    const exemption = await TaxExemptionService.revoke(app.db, id, request.user.id, reason)
    if (!exemption) return reply.status(404).send({ error: { message: 'Tax exemption not found', statusCode: 404 } })
    request.log.warn({ exemptionId: id, accountId: exemption.accountId, userId: request.user.id, reason }, 'Tax exemption revoked')
    return reply.send(exemption)
  })
}
