/**
 * Magic-link / pending-2FA temp token helpers — auth-2fa.
 *
 * magic_link_tokens is a multi-purpose table keyed by purpose discriminator:
 *   - 'magic_link'        : passwordless email login
 *   - 'pending_2fa'       : temp session awaiting 2FA verification
 *   - 'pending_2fa_setup' : temp session awaiting forced 2FA enrollment
 *
 * Only SHA-256 hashes of tokens are stored. The plaintext is returned to the
 * client once and held in memory (never localStorage) during the 2FA flow.
 */
import { and, eq, gt, isNull } from 'drizzle-orm'
import type { TenantId, UserId } from '@zync/types'
import type { Db } from '../client'
import { magicLinkTokens } from '../schema'

/**
 * Insert a pending temp session token (for 2FA challenge or forced setup).
 * `tokenHash` is SHA-256(plaintext). TTL is 10 minutes (caller sets expiresAt).
 * `tenantId` is required — real table has tenant_id NOT NULL.
 */
export async function insertMagicLinkToken(
  db: Db,
  args: {
    tenantId: TenantId
    userId: UserId
    tokenHash: string
    purpose: 'magic_link' | 'pending_2fa' | 'pending_2fa_setup'
    expiresAt: Date
  },
): Promise<void> {
  await db.insert(magicLinkTokens).values({
    tenantId: args.tenantId,
    userId: args.userId,
    tokenHash: args.tokenHash,
    purpose: args.purpose,
    expiresAt: args.expiresAt,
  })
}

/**
 * Look up an unused, non-expired magic-link/pending token by hash + purpose.
 * Returns null if missing, expired, or already used.
 */
export async function findPendingToken(
  db: Db,
  tokenHash: string,
  purpose: 'magic_link' | 'pending_2fa' | 'pending_2fa_setup',
) {
  const [row] = await db
    .select()
    .from(magicLinkTokens)
    .where(
      and(
        eq(magicLinkTokens.tokenHash, tokenHash),
        eq(magicLinkTokens.purpose, purpose),
        isNull(magicLinkTokens.usedAt),
        gt(magicLinkTokens.expiresAt, new Date()),
      ),
    )
    .limit(1)
  return row ?? null
}

/**
 * Atomically mark a magic-link/pending token as used (single-use).
 * Returns true when this call consumed the token; false if already used.
 */
export async function consumeMagicLinkToken(db: Db, tokenHash: string): Promise<boolean> {
  const result = await db
    .update(magicLinkTokens)
    .set({ usedAt: new Date() })
    .where(and(eq(magicLinkTokens.tokenHash, tokenHash), isNull(magicLinkTokens.usedAt)))
    .returning({ id: magicLinkTokens.id })
  return result.length > 0
}
