import "server-only"

/**
 * 微信公众号模板消息渠道
 *
 * 使用微信官方 API:
 *   1. 获取 access_token: GET https://api.weixin.qq.com/cgi-bin/token
 *   2. 发送模板消息: POST https://api.weixin.qq.com/cgi-bin/message/template/send
 *
 * access_token 带缓存（有效期 7200 秒，提前 5 分钟刷新），避免频繁请求。
 *
 * 环境变量:
 * - WECHAT_APP_ID: 公众号 AppID
 * - WECHAT_APP_SECRET: 公众号 AppSecret
 * - WECHAT_TEMPLATE_ID: 模板消息 ID
 *
 * 模板数据映射: payload.title -> keyword1, payload.content -> keyword2,
 * payload.type -> keyword3（可在微信公众号后台配置模板字段）。
 */

import type {
  NotificationPayload,
  ChannelSendResult,
  NotificationChannel,
} from "../types"
import type { NotificationChannelSender, ChannelRecipient } from "./types"

const channel: NotificationChannel = "wechat"
const WECHAT_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token"
const WECHAT_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send"

/** access_token 缓存（进程级） */
interface TokenCache {
  accessToken: string
  expiresAt: number // 毫秒时间戳
}
let tokenCache: TokenCache | null = null

/** 从环境变量读取微信配置 */
function getWechatConfig(): {
  appId: string | undefined
  appSecret: string | undefined
  templateId: string | undefined
} {
  return {
    appId: process.env.WECHAT_APP_ID,
    appSecret: process.env.WECHAT_APP_SECRET,
    templateId: process.env.WECHAT_TEMPLATE_ID,
  }
}

/** 是否启用微信渠道（配置完整才启用） */
export function isWechatEnabled(): boolean {
  const config = getWechatConfig()
  return Boolean(config.appId && config.appSecret && config.templateId)
}

/**
 * 获取微信 access_token（带缓存）。
 * 缓存有效期内的 token 直接复用；过期或不存在时重新请求。
 */
async function getAccessToken(): Promise<string> {
  const now = Date.now()
  // 缓存有效（提前 5 分钟刷新，避免边界过期）
  if (tokenCache && tokenCache.expiresAt - now > 5 * 60 * 1000) {
    return tokenCache.accessToken
  }

  const config = getWechatConfig()
  if (!config.appId || !config.appSecret) {
    throw new Error("WECHAT_APP_ID/WECHAT_APP_SECRET not configured")
  }

  const url = `${WECHAT_TOKEN_URL}?grant_type=client_credential&appid=${encodeURIComponent(
    config.appId
  )}&secret=${encodeURIComponent(config.appSecret)}`
  const res = await fetch(url, { method: "GET" })
  const data = (await res.json()) as {
    access_token?: string
    expires_in?: number
    errcode?: number
    errmsg?: string
  }

  if (!data.access_token) {
    throw new Error(
      `Failed to get WeChat access_token: ${data.errcode} ${data.errmsg ?? ""}`
    )
  }

  tokenCache = {
    accessToken: data.access_token,
    expiresAt: now + (data.expires_in ?? 7200) * 1000,
  }
  return tokenCache.accessToken
}

/**
 * 将通知负载映射为微信模板数据。
 * 默认映射: title -> keyword1, content -> keyword2, type -> keyword3。
 * 如需自定义映射，可在 metadata 中提供 wechatKeywords 覆盖。
 */
function buildTemplateData(
  payload: NotificationPayload
  ): Record<string, { value: string }> {
  // as 断言：metadata 为 Record<string, unknown>，此处需按 string 字典取值；
  // 运行时若类型不符会被 ?? 兜底为空对象，安全无副作用
  const custom = (payload.metadata?.wechatKeywords as Record<string, string> | undefined) ?? {}
  return {
    keyword1: { value: custom.keyword1 ?? payload.title },
    keyword2: { value: custom.keyword2 ?? payload.content },
    keyword3: { value: custom.keyword3 ?? payload.type },
  }
}

/** Mock 微信发送器（开发环境使用） */
class MockWechatSender implements NotificationChannelSender {
  readonly channel = channel

  async send(
    payload: NotificationPayload,
    recipient: ChannelRecipient
  ): Promise<ChannelSendResult> {
    console.info(
      `[MockWechat] send to openId=${recipient.wechatOpenId ?? "(no openId)"}: title="${payload.title}"`
    )
    return {
      channel,
      success: true,
      messageId: `mock-wechat-${Date.now()}`,
      sentAt: new Date(),
    }
  }

  async sendBatch(
    items: Array<{ payload: NotificationPayload; recipient: ChannelRecipient }>
  ): Promise<ChannelSendResult[]> {
    return Promise.all(items.map((item) => this.send(item.payload, item.recipient)))
  }
}

/** 微信公众号模板消息发送器 */
class WechatTemplateSender implements NotificationChannelSender {
  readonly channel = channel

  async send(
    payload: NotificationPayload,
    recipient: ChannelRecipient
  ): Promise<ChannelSendResult> {
    if (!recipient.wechatOpenId) {
      return { channel, success: false, error: "Missing recipient wechatOpenId", sentAt: new Date() }
    }

    const config = getWechatConfig()
    if (!config.templateId) {
      return { channel, success: false, error: "WECHAT_TEMPLATE_ID not configured", sentAt: new Date() }
    }

    try {
      const accessToken = await getAccessToken()
      const url = `${WECHAT_SEND_URL}?access_token=${encodeURIComponent(accessToken)}`
      const body = {
        touser: recipient.wechatOpenId,
        template_id: config.templateId,
        url: payload.actionUrl ?? "",
        data: buildTemplateData(payload),
      }

      const res = await fetch(url, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      })
      const data = (await res.json()) as {
        errcode?: number
        errmsg?: string
        msgid?: number
      }

      const success = data.errcode === 0
      return {
        channel,
        success,
        messageId: data.msgid != null ? String(data.msgid) : undefined,
        error: success ? undefined : `${data.errcode}: ${data.errmsg ?? ""}`,
        sentAt: new Date(),
      }
    } catch (e) {
      return {
        channel,
        success: false,
        error: e instanceof Error ? e.message : "WeChat send failed",
        sentAt: new Date(),
      }
    }
  }

  async sendBatch(
    items: Array<{ payload: NotificationPayload; recipient: ChannelRecipient }>
  ): Promise<ChannelSendResult[]> {
    return Promise.all(items.map((item) => this.send(item.payload, item.recipient)))
  }
}

/**
 * 创建微信渠道发送器。
 * 配置完整时使用真实发送器，否则使用 Mock 实现。
 */
export function createWechatSender(): NotificationChannelSender {
  if (isWechatEnabled()) {
    return new WechatTemplateSender()
  }
  return new MockWechatSender()
}
