/**
 * TelegramNotificationAdapter — system-communications-notifications (Task 7).
 *
 * Delivers notifications via Telegram Bot API using the tenant's own bot token
 * (stored encrypted in adapter_credentials). Requires Business tier (gated at
 * the route/settings layer via requireTier('business')).
 */
import type {
  NotificationAdapter,
  DeliverableNotification,
  DeliveryResult,
  NotificationType,
} from '@zync/types'
import type { Db } from '@zync/db/queries'
import type { Env } from '@zync/types'
import {
  getUserTelegramPrefs,
  getUserTelegramChatId,
} from '@zync/db/queries'
import { loadAdapterCredential } from '../credentials'

interface TelegramTextMessage {
  chat_id: string
  text: string
  parse_mode?: 'HTML' | 'Markdown'
  reply_markup?: {
    inline_keyboard: Array<Array<{ text: string; url?: string; callback_data?: string }>>
  }
}

async function telegramSendMessage(
  token: string,
  msg: TelegramTextMessage,
): Promise<void> {
  const res = await fetch(
    `https://api.telegram.org/bot${token}/sendMessage`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(msg),
    },
  )
  if (!res.ok) {
    const text = await res.text()
    throw new Error(`Telegram sendMessage failed: ${res.status} ${text}`)
  }
}

export class TelegramNotificationAdapter implements NotificationAdapter {
  readonly id = 'telegram' as const

  constructor(
    private readonly db: Db,
    private readonly env: Env,
    private readonly tenantId: string,
  ) {}

  async canDeliver(userId: string, type: NotificationType): Promise<boolean> {
    try {
      // Check user has telegram type opted-in
      const prefs = await getUserTelegramPrefs(this.db, userId, this.tenantId)
      if (!prefs) return false
      if (!prefs.telegramTypes.includes(type)) return false

      // Check user has a linked Telegram chat
      const chatId = await getUserTelegramChatId(this.db, userId, this.tenantId)
      if (!chatId) return false

      // Check tenant has a bot token configured
      const token = await loadAdapterCredential(
        this.db,
        this.tenantId,
        'telegram',
        this.env.INTEGRATION_ENCRYPTION_KEY,
      )
      return !!token
    } catch {
      return false
    }
  }

  async deliver(userId: string, notification: DeliverableNotification): Promise<DeliveryResult> {
    try {
      const canSend = await this.canDeliver(userId, notification.type)
      if (!canSend) return { delivered: false }

      const chatId = await getUserTelegramChatId(this.db, userId, this.tenantId)
      if (!chatId) return { delivered: false, error: 'No Telegram chat linked' }

      const token = await loadAdapterCredential(
        this.db,
        this.tenantId,
        'telegram',
        this.env.INTEGRATION_ENCRYPTION_KEY,
      )
      if (!token) return { delivered: false, error: 'No Telegram bot token' }

      // Build inline keyboard: URL buttons as deep links, callbackAction buttons as inline (DM only)
      const buttons =
        notification.actionButtons?.map((b) => {
          if (b.url) {
            return { text: b.label, url: b.url }
          }
          // callbackAction for inline keyboard
          return { text: b.label, callback_data: b.callbackAction ?? b.label }
        }) ?? []

      const msgText = `<b>${escapeHtml(notification.title)}</b>\n${escapeHtml(notification.body)}`

      await telegramSendMessage(token, {
        chat_id: chatId,
        text: msgText,
        parse_mode: 'HTML',
        ...(buttons.length > 0
          ? { reply_markup: { inline_keyboard: [buttons] } }
          : {}),
      })

      return { delivered: true }
    } catch (err) {
      return {
        delivered: false,
        error: err instanceof Error ? err.message : String(err),
      }
    }
  }
}

function escapeHtml(text: string): string {
  return text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
}
