/**
 * Telegram bot routes — telegram-bot (wave-8 leaf9).
 * Mounted at /api/telegram in routes/index.ts.
 *
 * POST /webhook        — receive Telegram bot updates (no auth, verified by token)
 * GET  /chats          — list linked chats for the tenant
 * DELETE /chats/:chatId — unlink (soft-deactivate) a chat
 *
 * Bot commands handled in webhook:
 *   /start   — link chat, return greeting
 *   /status  — today's open invoice count + overdue count
 *   /invoices — list last 5 invoices
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission } from '../../middleware/guards'
import { timingSafeEqual } from '@zync/auth'
import { claimInboundWebhookEvent } from '../../lib/webhook-idempotency'
import {
  createDb,
  getTelegramChats,
  linkTelegramChat,
  unlinkTelegramChat,
  getTelegramChatByChatId,
  listInvoices,
} from '@zync/db/queries'

export const telegramRoutes = new Hono<AppEnv>()

// ── Telegram Update schema ────────────────────────────────────────────────────

const TelegramMessageSchema = z.object({
  message_id: z.number(),
  from: z
    .object({
      id: z.number(),
      username: z.string().optional(),
      first_name: z.string().optional(),
    })
    .optional(),
  chat: z.object({
    id: z.number(),
    type: z.enum(['private', 'group', 'supergroup', 'channel']),
    title: z.string().optional(),
    username: z.string().optional(),
    first_name: z.string().optional(),
  }),
  text: z.string().optional(),
})

const TelegramUpdateSchema = z.object({
  update_id: z.number(),
  message: TelegramMessageSchema.optional(),
})

// ── POST /telegram/webhook ────────────────────────────────────────────────────

telegramRoutes.post('/webhook', async (c) => {
  // Mandatory secret — unset env disables the public webhook surface (fail closed).
  const expectedToken = (c.env as unknown as Record<string, string>).TELEGRAM_WEBHOOK_SECRET ?? ''
  if (!expectedToken) {
    console.error('[telegram-webhook] TELEGRAM_WEBHOOK_SECRET is not configured')
    return c.json({ error: 'Webhook not configured' }, 503)
  }
  const headerToken = c.req.header('X-Telegram-Bot-Api-Secret-Token') ?? ''
  if (!timingSafeEqual(headerToken, expectedToken)) {
    return c.json({ error: 'Unauthorized' }, 403)
  }

  const body = await c.req.json().catch(() => null)
  const parsed = TelegramUpdateSchema.safeParse(body)
  if (!parsed.success || !parsed.data.message) {
    // Telegram expects 200 even for unhandled updates
    return c.json({ ok: true }, 200)
  }

  const claimed = await claimInboundWebhookEvent(c.env.KV, 'telegram', String(parsed.data.update_id))
  if (!claimed) return c.json({ ok: true }, 200)

  const { message } = parsed.data
  const chatId = String(message.chat.id)
  const _chatType = message.chat.type
  const _chatTitle = message.chat.title ?? message.chat.first_name ?? null
  const text = (message.text ?? '').trim()

  // Only handle commands
  if (!text.startsWith('/')) {
    return c.json({ ok: true }, 200)
  }

  const db = createDb(c.env)
  const command = text.split(' ')[0]?.toLowerCase()

  // ── /start ──────────────────────────────────────────────────────────────────
  if (command === '/start') {
    const existingChat = await getTelegramChatByChatId(db, chatId)
    if (!existingChat) {
      // Chat not linked — respond with instructions, linking happens via settings UI
      await sendTelegramMessage(c.env, chatId, [
        'Welcome to Zync! 🎉',
        '',
        'To link this chat to your Zync workspace, go to:',
        'Settings → Integrations → Telegram',
        '',
        'Once linked, you can use:',
        '/status — view open & overdue invoices',
        '/invoices — list your last 5 invoices',
      ].join('\n'))
    } else {
      await sendTelegramMessage(c.env, chatId, 'This chat is already linked to your Zync workspace. Use /status or /invoices to get started.')
    }
    return c.json({ ok: true }, 200)
  }

  // Remaining commands require a linked chat with a known tenantId
  const chat = await getTelegramChatByChatId(db, chatId)
  if (!chat || !chat.is_active) {
    await sendTelegramMessage(c.env, chatId, 'This chat is not linked to a Zync workspace. Use /start to begin.')
    return c.json({ ok: true }, 200)
  }

  const tenantId = chat.tenant_id

  // ── /status ──────────────────────────────────────────────────────────────────
  if (command === '/status') {
    try {
      const today = new Date()
      const todayStr = today.toISOString().slice(0, 10)

      // Fetch sent invoices (open) and due-before-today invoices (overdue)
      const [sentResult, overdueResult] = await Promise.all([
        listInvoices(db, tenantId, { status: 'SENT', limit: 1 }),
        listInvoices(db, tenantId, { status: 'SENT', dateTo: todayStr, limit: 1 }),
      ])

      const lines = [
        `*Zync Status — ${todayStr}*`,
        '',
        `Open invoices: ${sentResult.total}`,
        `Overdue invoices: ${overdueResult.total}`,
      ]
      await sendTelegramMessage(c.env, chatId, lines.join('\n'))
    } catch {
      await sendTelegramMessage(c.env, chatId, 'Could not fetch status. Please try again later.')
    }
    return c.json({ ok: true }, 200)
  }

  // ── /invoices ────────────────────────────────────────────────────────────────
  if (command === '/invoices') {
    try {
      const result = await listInvoices(db, tenantId, { limit: 5 })
      if (!result.items.length) {
        await sendTelegramMessage(c.env, chatId, 'No invoices found.')
        return c.json({ ok: true }, 200)
      }

      const lines = ['*Last 5 Invoices*', '']
      for (const inv of result.items) {
        lines.push(`• #${inv.invoiceNumber ?? inv.id.slice(0, 8)} — ${inv.status} — ${inv.total} ${inv.currency}`)
      }
      await sendTelegramMessage(c.env, chatId, lines.join('\n'))
    } catch {
      await sendTelegramMessage(c.env, chatId, 'Could not fetch invoices. Please try again later.')
    }
    return c.json({ ok: true }, 200)
  }

  // Unknown command — silent 200 (Telegram expects 200 for all updates)
  return c.json({ ok: true }, 200)
})

// ── GET /telegram/chats ───────────────────────────────────────────────────────

telegramRoutes.get('/chats', authMiddleware, requirePermission('settings:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = createDb(c.env)
  const chats = await getTelegramChats(db, session.tid)
  return c.json({ chats }, 200)
})

// ── DELETE /telegram/chats/:chatId ────────────────────────────────────────────

telegramRoutes.delete('/chats/:chatId', authMiddleware, requirePermission('settings:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const { chatId } = c.req.param()
  if (!chatId) {
    return c.json({ error: 'Missing chatId' }, 400)
  }

  const db = createDb(c.env)
  const unlinked = await unlinkTelegramChat(db, session.tid, chatId)
  if (!unlinked) {
    return c.json({ error: 'Chat not found' }, 404)
  }

  return c.json({ ok: true }, 200)
})

// ── POST /telegram/chats ─────────────────────────────────────────────────────
// Manual link endpoint (for settings UI "link by chat ID")

const linkChatSchema = z.object({
  chat_id: z.string().min(1),
  chat_type: z.enum(['private', 'group', 'supergroup', 'channel']),
  chat_title: z.string().optional(),
})

telegramRoutes.post('/chats', authMiddleware, requirePermission('settings: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 parsed = linkChatSchema.safeParse(body)
  if (!parsed.success) {
    return c.json({ error: 'Validation failed', issues: parsed.data }, 400)
  }

  const db = createDb(c.env)
  const chat = await linkTelegramChat(
    db,
    session.tid,
    parsed.data.chat_id,
    parsed.data.chat_type,
    parsed.data.chat_title ?? null,
    session.sub,
  )

  return c.json({ chat }, 201)
})

// ── Internal helper ───────────────────────────────────────────────────────────

async function sendTelegramMessage(
  env: AppEnv['Bindings'],
  chatId: string,
  text: string,
): Promise<void> {
  const token = (env as unknown as Record<string, string>).TELEGRAM_BOT_TOKEN
  if (!token) return

  try {
    await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        chat_id: chatId,
        text,
        parse_mode: 'Markdown',
      }),
    })
  } catch {
    // Best-effort — don't throw on Telegram send errors
  }
}
