import crypto from 'crypto'
import { getDb } from '../client'
import { weixinContextTokens } from '../schema'
import { eq, and } from 'drizzle-orm'
import { nowExpr } from '../shared/dialect-helpers'

/**
 * Weixin Context Token Repository。
 *
 * 封装 weixin_context_tokens 表的所有查询。每行保存 (channel_id, peer_user_id)
 * → context_token 的映射，用于在 outbound 发消息时复用上游返回的会话上下文。
 *
 * 设计约定：
 * - 返回类型由 drizzle 推断，消费方无需 cast
 * - UPDATE 显式刷新 updated_at 用 nowExpr()
 * - 无独立删除——其生命周期跟随 im_channel，由 imChannelRepo.deleteById
 *   事务级联清理
 */

export const weixinContextTokenRepo = {
  /** 按 channel + peer 查 token（send/sendImage/sendFile 时复用） */
  findToken(channelId: string, peerUserId: string) {
    return getDb()
      .select({ contextToken: weixinContextTokens.contextToken })
      .from(weixinContextTokens)
      .where(
        and(
          eq(weixinContextTokens.channelId, channelId),
          eq(weixinContextTokens.peerUserId, peerUserId),
        ),
      )
      .get()
  },

  /**
   * Upsert：首次写入或刷新已有行的 context_token。
   * 原 SQL 用 `ON CONFLICT(channel_id, peer_user_id) DO UPDATE` ——迁移到
   * drizzle 后由渠道事务等价实现：先 select 判存在再分支 insert/update，
   * 两条 SQL 由 db.transaction 保证原子性（与原 INSERT...ON CONFLICT 行为一致）。
   *
   * 原 race-condition 容忍语义：setContextToken 自身 try/catch 失败不抛，
   * 此处保持同一行为——upsert 失败由调用方 try/catch 兜底。
   */
  upsert(channelId: string, peerUserId: string, token: string) {
    getDb().transaction((tx) => {
      const existing = tx
        .select({ id: weixinContextTokens.id })
        .from(weixinContextTokens)
        .where(
          and(
            eq(weixinContextTokens.channelId, channelId),
            eq(weixinContextTokens.peerUserId, peerUserId),
          ),
        )
        .get()
      if (existing) {
        tx.update(weixinContextTokens)
          .set({ contextToken: token, updatedAt: nowExpr() })
          .where(eq(weixinContextTokens.id, existing.id))
          .run()
      } else {
        tx.insert(weixinContextTokens)
          .values({
            id: crypto.randomUUID(),
            channelId,
            peerUserId,
            contextToken: token,
          })
          .run()
      }
    })
  },
}
