/**
 * Recurring invoice template routes — recurring-invoices (P061).
 * Mounted at /api/recurring-invoices in apps/zync-api/src/index.ts.
 *
 * Routes:
 *   GET    /                      → list templates (tenant-scoped, filterable by status)
 *   POST   /                      → create template (Business+ tier required)
 *   GET    /:id                   → get single template
 *   PATCH  /:id                   → update template
 *   DELETE /:id                   → cancel template
 *   POST   /:id/generate-now      → manual trigger (max 1/hour per template, rate-limited by KV)
 *   GET    /:id/invoices           → list invoices generated from this template
 *
 * Tier gate:
 *   Business: max 20 active templates
 *   Enterprise: unlimited
 *   Free/Starter: 402 via requireTier
 *
 * Permissions:
 *   View:              any authenticated member (invoices:read implied by membership)
 *   Create/Edit:       invoices:write
 *   Pause/Cancel:      OWNER | ADMIN only
 *   Generate Now:      invoices:write
 *   Delete (cancel):   OWNER | ADMIN only
 */
import { Hono, type Context } from 'hono'
import {
  listRecurringInvoiceTemplates,
  getRecurringInvoiceTemplate,
  createRecurringInvoiceTemplate,
  updateRecurringInvoiceTemplate,
  cancelRecurringInvoiceTemplate,
  countActiveRecurringTemplates,
  listRecurringInvoiceTemplatesSchema,
  generateInvoiceFromTemplate,
  previewNextDates,
  createRecurringInvoiceSchema,
  updateRecurringInvoiceSchema,
  sendInvoice,
  listInvoices,
  listInvoicesSchema,
  assertTenantOwnsCustomer,
  invalidTenantReferenceBody,
  createNotification,
} from '@zync/db/queries'
import { sql, type Db } from '@zync/db'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission, requireTier } from '../../middleware/guards'
import { requireModuleEnabled } from '../../middleware/require-module-enabled'
import { TenantTier } from '@zync/types'

const recurringInvoiceRoutes = new Hono<AppEnv>()

recurringInvoiceRoutes.use('*', authMiddleware)
recurringInvoiceRoutes.use('*', requireModuleEnabled('invoices'))
recurringInvoiceRoutes.use('*', requireTier(TenantTier.BUSINESS))

function generateNowRateLimitKey(templateId: string): string {
  return `recurring-invoice:generate-now:${templateId}`
}

function getTenantCountry(c: Context<AppEnv>): string {
  const session = c.get('session')
  if (session && 'countryCode' in session && typeof session.countryCode === 'string') {
    return session.countryCode
  }
  return 'IL'
}

async function notifyRecurringTemplateCompleted(
  db: Db,
  tenantId: string,
  templateTitle: string,
  templateId: string,
): Promise<void> {
  try {
    const result = await db.execute(
      sql`SELECT tm.user_id
          FROM tenant_memberships tm
          JOIN roles r ON r.id = tm.role_id
          WHERE tm.tenant_id = ${tenantId}
            AND r.name = 'OWNER'
            AND tm.status = 'active'
          LIMIT 1`,
    )
    const row = (result as unknown as { user_id?: string }[])[0]
    if (!row?.user_id) return

    await createNotification(db, {
      tenantId,
      userId: row.user_id,
      type: 'recurring_invoice_completed',
      titleKey: 'notifications.recurring_invoice_completed.title',
      bodyKey: 'notifications.recurring_invoice_completed.body',
      params: { templateTitle, templateId },
      entityType: 'recurring_invoice_template',
      entityId: templateId,
    })
  } catch (err) {
    console.error(`[recurring-invoices] notifyRecurringTemplateCompleted error: ${err}`)
  }
}

// ── GET / — list templates ────────────────────────────────────────────────────

recurringInvoiceRoutes.get('/', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const query = c.req.query()
  const parsed = listRecurringInvoiceTemplatesSchema.safeParse({
    cursor: query.cursor,
    limit: query.limit,
    status: query.status,
    customerId: query.customer_id ?? query.customerId,
  })

  if (!parsed.success) {
    return c.json({ error: 'Validation failed', issues: parsed.error.issues }, 400)
  }

  const db = c.get('db')
  const result = await listRecurringInvoiceTemplates(db, session.tid, parsed.data)
  return c.json(result, 200)
})

// ── POST / — create template ──────────────────────────────────────────────────

recurringInvoiceRoutes.post('/', requirePermission('invoices:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const body = await c.req.json().catch(() => null)
  const wantsAutoCharge =
    body != null &&
    typeof body === 'object' &&
    (body.auto_charge === true || body.autoCharge === true)

  const parsed = createRecurringInvoiceSchema.safeParse(body)
  if (!parsed.success) {
    return c.json({ error: 'Validation failed', issues: parsed.error.issues }, 400)
  }

  const db = c.get('db')
  const tier = session.tier

  if (tier === TenantTier.BUSINESS) {
    const activeCount = await countActiveRecurringTemplates(db, session.tid)
    if (activeCount >= 20) {
      return c.json(
        {
          error: 'Active template limit reached',
          message: 'Business plan allows up to 20 active recurring invoice templates. Upgrade to Enterprise for unlimited templates.',
          activeCount,
          limit: 20,
        },
        403,
      )
    }
  }

  if (!(await assertTenantOwnsCustomer(db, session.tid, parsed.data.customerId))) {
    return c.json(invalidTenantReferenceBody('customerId'), 400)
  }

  const template = await createRecurringInvoiceTemplate(db, session.tid, session.sub, parsed.data)
  const nextDates = previewNextDates(template.startDate, template.frequency, template.frequencyDay)

  return c.json(
    {
      template,
      nextDates,
      ...(wantsAutoCharge
        ? { warning: 'auto_charge is not yet supported and will be ignored' }
        : {}),
    },
    201,
  )
})

// ── GET /:id — get template ───────────────────────────────────────────────────

recurringInvoiceRoutes.get('/:id', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const id = c.req.param('id')
  const db = c.get('db')

  const template = await getRecurringInvoiceTemplate(db, session.tid, id)
  if (!template) {
    return c.json({ error: 'Not found' }, 404)
  }

  const nextDates = previewNextDates(template.startDate, template.frequency, template.frequencyDay)

  return c.json({ template, nextDates })
})

// ── PATCH /:id — update template ──────────────────────────────────────────────

recurringInvoiceRoutes.patch('/:id', requirePermission('invoices:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const id = c.req.param('id')

  const body = await c.req.json().catch(() => null)
  const wantsAutoCharge =
    body != null &&
    typeof body === 'object' &&
    (body.auto_charge === true || body.autoCharge === true)

  const parsed = updateRecurringInvoiceSchema.safeParse(body)
  if (!parsed.success) {
    return c.json({ error: 'Validation failed', issues: parsed.error.issues }, 400)
  }

  const isPauseOrCancel =
    parsed.data.status === 'paused' || parsed.data.status === 'cancelled'
  if (isPauseOrCancel) {
    const role = session.role ?? ''
    if (!['OWNER', 'ADMIN'].includes(role.toUpperCase())) {
      return c.json({ error: 'Forbidden: only OWNER or ADMIN can pause/cancel templates' }, 403)
    }
  }

  const db = c.get('db')
  if (parsed.data.customerId !== undefined) {
    if (!(await assertTenantOwnsCustomer(db, session.tid, parsed.data.customerId))) {
      return c.json(invalidTenantReferenceBody('customerId'), 400)
    }
  }
  const template = await updateRecurringInvoiceTemplate(db, session.tid, id, parsed.data)

  const nextDates = previewNextDates(template.startDate, template.frequency, template.frequencyDay)

  return c.json(
    {
      template,
      nextDates,
      ...(wantsAutoCharge
        ? { warning: 'auto_charge is not yet supported and will be ignored' }
        : {}),
    },
  )
})

// ── DELETE /:id — cancel template ────────────────────────────────────────────

recurringInvoiceRoutes.delete('/:id', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const role = session.role ?? ''
  if (!['OWNER', 'ADMIN'].includes(role.toUpperCase())) {
    return c.json({ error: 'Forbidden: only OWNER or ADMIN can cancel templates' }, 403)
  }

  const id = c.req.param('id')
  const db = c.get('db')

  const existing = await getRecurringInvoiceTemplate(db, session.tid, id)
  if (!existing) {
    return c.json({ error: 'Not found' }, 404)
  }

  await cancelRecurringInvoiceTemplate(db, session.tid, id)
  return c.json({ status: 'cancelled' })
})

// ── POST /:id/generate-now — manual trigger ───────────────────────────────────

recurringInvoiceRoutes.post('/:id/generate-now', requirePermission('invoices:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const id = c.req.param('id')
  const db = c.get('db')

  const template = await getRecurringInvoiceTemplate(db, session.tid, id)
  if (!template) {
    return c.json({ error: 'Not found' }, 404)
  }
  if (template.status !== 'active') {
    return c.json({ error: 'Template is not active' }, 409)
  }

  const kv = c.env.KV
  const rateLimitKey = generateNowRateLimitKey(id)
  const lastGenerated = await kv.get(rateLimitKey)
  if (lastGenerated) {
    return c.json(
      {
        error: 'Rate limit exceeded',
        message: 'Manual generation is limited to once per hour per template.',
        retryAfter: lastGenerated,
      },
      429,
    )
  }

  const countryCode = getTenantCountry(c)
  const result = await generateInvoiceFromTemplate(db, template, session.sub, countryCode)

  if (result.skipped) {
    return c.json(
      { error: 'Invoice already generated for this period or template has no line items' },
      409,
    )
  }

  if (template.autoSend && result.invoiceId) {
    await sendInvoice(
      db,
      session.tid,
      result.invoiceId,
      session.sub,
      countryCode,
      template.nextGenerationDate,
    )
  }

  if (result.completed) {
    await notifyRecurringTemplateCompleted(db, session.tid, template.title, template.id)
  }

  const oneHourFromNow = new Date(Date.now() + 3600_000).toISOString()
  await kv.put(rateLimitKey, oneHourFromNow, { expirationTtl: 3600 })

  return c.json({ invoiceId: result.invoiceId, completed: result.completed }, 200)
})

// ── GET /:id/invoices — list generated invoices ───────────────────────────────

recurringInvoiceRoutes.get('/:id/invoices', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const id = c.req.param('id')
  const db = c.get('db')

  const template = await getRecurringInvoiceTemplate(db, session.tid, id)
  if (!template) {
    return c.json({ error: 'Not found' }, 404)
  }

  const query = c.req.query()
  const parsed = listInvoicesSchema.safeParse({
    cursor: query.cursor,
    limit: query.limit,
  })

  if (!parsed.success) {
    return c.json({ error: 'Validation failed', issues: parsed.error.issues }, 400)
  }

  const result = await listInvoices(db, session.tid, {
    ...parsed.data,
    recurringTemplateId: id,
  })
  return c.json(result, 200)
})

export { recurringInvoiceRoutes }
