import { getDb } from '../client'
import { invitations, user } from '../schema'
import { eq, and, desc, inArray, sql } from 'drizzle-orm'
import { nowExpr } from '../shared/dialect-helpers'

/**
 * Invitation Repository。封装 invitations 表的所有查询。
 *
 * 设计约定：
 * - 返回类型由 drizzle 推断，消费方不再写 `as Record<string, unknown>`
 * - CAS（compare-and-swap）状态转换用 and() 组合条件，返回 changes 数供
 *   调用方判断是否成功转换（poll 路由依赖此机制实现并发互斥）
 * - 单调状态机：所有跨状态推进都带 `WHERE status = '<expected>'` 守卫
 * - 时间刷新统一用 nowExpr()
 *
 * 来源路由：
 *   - GET/POST /api/invite
 *   - /api/invite/[token]/{poll,start-qr,public,verify-phone}
 *   - lib/invite/lifecycle.ts（级联删除）
 */

/** CAS UPDATE 的执行结果，调用方据此判断是否赢得状态转换。 */
export interface CasResult {
  changes: number
  lastInsertRowid: number | bigint
}

export const invitationRepo = {
  /** 列出 inviter 的所有邀请，按 created_at 倒序（GET /api/invite） */
  listByInviter(inviterUserId: string) {
    return getDb()
      .select({
        id: invitations.id,
        inviteToken: invitations.inviteToken,
        inviteePhone: invitations.inviteePhone,
        inviteeName: invitations.inviteeName,
        status: invitations.status,
        projectRoot: invitations.projectRoot,
        createdChannelId: invitations.createdChannelId,
        createdAt: invitations.createdAt,
        updatedAt: invitations.updatedAt,
      })
      .from(invitations)
      .where(eq(invitations.inviterUserId, inviterUserId))
      .orderBy(desc(invitations.createdAt))
      .all()
  },

  /** 按 invite_token 查（poll/start-qr/public/verify-phone 公共入口） */
  findByInviteToken(inviteToken: string) {
    return getDb()
      .select()
      .from(invitations)
      .where(eq(invitations.inviteToken, inviteToken))
      .get()
  },

  /** 按 id 查 */
  findById(id: string) {
    return getDb().select().from(invitations).where(eq(invitations.id, id)).get()
  },

  /**
   * 查 inviter 在 sinceUnixSec 之后创建的最近一条 pending 邀请
   * （POST /api/invite 的 React StrictMode 双触发去重）。
   *
   * 原 SQL：`... WHERE inviter_user_id = ? AND status = 'pending'
   *          AND created_at >= datetime(?, 'unixepoch')
   *          ORDER BY created_at DESC LIMIT 1`
   */
  findRecentPendingByInviter(inviterUserId: string, sinceUnixSec: number) {
    return getDb()
      .select({
        id: invitations.id,
        inviteToken: invitations.inviteToken,
        expiresAt: invitations.expiresAt,
      })
      .from(invitations)
      .where(
        and(
          eq(invitations.inviterUserId, inviterUserId),
          eq(invitations.status, 'pending'),
          sql`${invitations.createdAt} >= datetime(${sinceUnixSec}, 'unixepoch')`,
        ),
      )
      .orderBy(desc(invitations.createdAt))
      .limit(1)
      .get()
  },

  /** 创建邀请（POST /api/invite）。status 由 schema 默认 'pending' */
  create(data: {
    id: string
    inviteToken: string
    inviterUserId: string
    projectRoot: string
    expiresAt: number
  }) {
    getDb()
      .insert(invitations)
      .values({
        id: data.id,
        inviteToken: data.inviteToken,
        inviterUserId: data.inviterUserId,
        projectRoot: data.projectRoot,
        expiresAt: data.expiresAt,
      })
      .run()
    return this.findById(data.id)
  },

  // ── CAS 状态转换 ────────────────────────────────────────────────
  // 所有方法都返回 changes 数；调用方据此判断是否赢得转换。
  // WHERE 子句中的 status 守卫是状态机单调性的唯一来源 —— 不能依赖
  // 内存快照，因为 poll 之间的网络往返可能跨越多个状态推进。

  /**
   * CAS：qr_generated → bot_scanned（poll 扫码确认）。
   * 原 SQL：`UPDATE invitations SET status = 'bot_scanned', updated_at = ...
   *          WHERE id = ? AND status = 'qr_generated'`
   */
  casToBotScanned(id: string): CasResult {
    const res = getDb()
      .update(invitations)
      .set({ status: 'bot_scanned', updatedAt: nowExpr() })
      .where(and(eq(invitations.id, id), eq(invitations.status, 'qr_generated')))
      .run()
    return { changes: res.changes, lastInsertRowid: res.lastInsertRowid }
  },

  /**
   * CAS：qr_generated|bot_scanned → bot_connected，同时落 bot 凭证
   * （poll 确认授权的并发互斥点，唯一一个 poll 能拿到 changes === 1）。
   *
   * 原 SQL：`UPDATE invitations SET status = 'bot_connected',
   *          bot_token = ?, ilink_bot_id = ?, baseurl = ?, updated_at = ...
   *          WHERE id = ? AND status IN ('qr_generated', 'bot_scanned')`
   */
  casToBotConnected(
    id: string,
    creds: { botToken: string; ilinkBotId: string; baseurl: string },
  ): CasResult {
    const res = getDb()
      .update(invitations)
      .set({
        status: 'bot_connected',
        botToken: creds.botToken,
        ilinkBotId: creds.ilinkBotId,
        baseurl: creds.baseurl,
        updatedAt: nowExpr(),
      })
      .where(
        and(
          eq(invitations.id, id),
          inArray(invitations.status, ['qr_generated', 'bot_scanned']),
        ),
      )
      .run()
    return { changes: res.changes, lastInsertRowid: res.lastInsertRowid }
  },

  /**
   * CAS：bot_connected → completed，落 provision 产物引用
   * （provision 成功后的最终落库；守卫保证只有赢得上一步 CAS 的 poll 才能到达）。
   *
   * 原 SQL：`UPDATE invitations SET status = 'completed',
   *          created_user_id = ?, created_user_dir = ?, created_channel_id = ?,
   *          updated_at = ... WHERE id = ? AND status = 'bot_connected'`
   */
  casToCompleted(
    id: string,
    provision: { createdUserId: string; createdUserDir: string; createdChannelId: string },
  ): CasResult {
    const res = getDb()
      .update(invitations)
      .set({
        status: 'completed',
        createdUserId: provision.createdUserId,
        createdUserDir: provision.createdUserDir,
        createdChannelId: provision.createdChannelId,
        updatedAt: nowExpr(),
      })
      .where(and(eq(invitations.id, id), eq(invitations.status, 'bot_connected')))
      .run()
    return { changes: res.changes, lastInsertRowid: res.lastInsertRowid }
  },

  // ── 非 CAS 状态推进（无并发竞态，但仍刷新 updated_at） ─────────

  /** 标记 phone_verified 并记录手机号（verify-phone 校验通过） */
  markPhoneVerified(id: string, phone: string) {
    getDb()
      .update(invitations)
      .set({ status: 'phone_verified', inviteePhone: phone, updatedAt: nowExpr() })
      .where(eq(invitations.id, id))
      .run()
  },

  /** 标记 qr_generated 并落二维码（start-qr 成功） */
  markQrGenerated(id: string, qrcodeKey: string, qrcodeUrl: string) {
    getDb()
      .update(invitations)
      .set({
        status: 'qr_generated',
        qrcodeKey,
        qrcodeUrl,
        updatedAt: nowExpr(),
      })
      .where(eq(invitations.id, id))
      .run()
  },

  /** 标记过期（public/start-qr/verify-phone 检测到 expires_at 已过） */
  markExpired(id: string) {
    getDb()
      .update(invitations)
      .set({ status: 'expired', updatedAt: nowExpr() })
      .where(eq(invitations.id, id))
      .run()
  },

  /** 按 id 删除（lib/invite/lifecycle.ts 级联删除的一环） */
  deleteById(id: string) {
    getDb().delete(invitations).where(eq(invitations.id, id)).run()
  },

  /**
   * 按 invite_token 查 inviter 的用户名（GET /api/invite/[token]/public 展示邀请人）。
   * 原 SQL：`SELECT u.name FROM invitations i JOIN user u ON u.id = i.inviter_user_id
   *          WHERE i.invite_token = ?`
   */
  findInviterNameByInviteToken(inviteToken: string) {
    return getDb()
      .select({ name: user.name })
      .from(invitations)
      .innerJoin(user, eq(user.id, invitations.inviterUserId))
      .where(eq(invitations.inviteToken, inviteToken))
      .get()
  },

  /** 标记取消（DELETE /api/invite/[token]，inviter 主动撤销） */
  markCancelled(id: string) {
    getDb()
      .update(invitations)
      .set({ status: 'cancelled', updatedAt: nowExpr() })
      .where(eq(invitations.id, id))
      .run()
  },
}
