/**
 * Storage quota helpers — zync-subscription spec (Task 6).
 *
 * Enforces per-tier storage limits before R2 writes.
 * Counter key: 'storage_bytes', period: 'all_time' (lifetime bytes stored).
 *
 * Quota limits:
 *   freelancer:  1 GB  (1_073_741_824 bytes)
 *   business:    20 GB (21_474_836_480 bytes)
 *   enterprise:  100 GB (107_374_182_400 bytes)
 *   white_label: 100 GB (same as enterprise)
 *
 * Usage:
 *   Before write:  await checkStorageQuota(tenantId, fileBytes, tier, db)
 *   After write:   await incrementCounter(db, tenantId, 'storage_bytes', 'all_time')
 *                  (note: incrementCounter increments by 1; for bytes use incrementCounterBy
 *                   if available, or call the raw helper below)
 *   After delete:  await decrementCounter(db, tenantId, 'storage_bytes', 'all_time', fileBytes)
 *
 * NOTE: incrementCounter in @zync/db increments by 1. For byte-level tracking,
 * routes that upload files should directly use getCounterValue / decrementCounter
 * from @zync/db/queries and raw SQL for increment-by-N until incrementCounterBy
 * is available. The quota check itself only reads the counter.
 */
import { TenantTier } from '@zync/types'
import type { Db } from '@zync/db/queries'
import { getCounterValue, QuotaExceededError } from '@zync/db/queries'

// ---------------------------------------------------------------------------
// Quota limits
// ---------------------------------------------------------------------------

const STORAGE_QUOTA_BYTES: Record<TenantTier, number> = {
  [TenantTier.FREELANCER]:  1_073_741_824,    // 1 GB
  [TenantTier.BUSINESS]:   21_474_836_480,    // 20 GB
  [TenantTier.ENTERPRISE]: 107_374_182_400,   // 100 GB
  [TenantTier.WHITE_LABEL]: 107_374_182_400,  // 100 GB
}

/**
 * Returns the storage quota in bytes for a given tier.
 */
export function getStorageQuotaBytes(tier: TenantTier): number {
  return STORAGE_QUOTA_BYTES[tier] ?? 1_073_741_824
}

// ---------------------------------------------------------------------------
// Storage quota error
// ---------------------------------------------------------------------------

export interface StorageQuotaDetails {
  usedBytes: number
  limitBytes: number
  requestedBytes: number
}

/**
 * Thrown when a storage upload would exceed the tenant's quota.
 * Extends QuotaExceededError for catch-site compatibility.
 * Also carries structured details for the 402 response body.
 */
export class StorageQuotaExceededError extends QuotaExceededError {
  readonly usedBytes: number
  readonly limitBytes: number
  readonly requestedBytes: number

  constructor(details: StorageQuotaDetails) {
    super('storage', details.limitBytes, details.usedBytes)
    this.name = 'StorageQuotaExceededError'
    this.usedBytes = details.usedBytes
    this.limitBytes = details.limitBytes
    this.requestedBytes = details.requestedBytes
  }
}

// ---------------------------------------------------------------------------
// Enforcement function
// ---------------------------------------------------------------------------

/**
 * Check whether a prospective upload would exceed the tenant's storage quota.
 * Throws `StorageQuotaExceededError` if `current + uploadBytes > limit`.
 * Passes silently (returns void) if within quota.
 *
 * Must be called BEFORE writing to R2.
 * Callers are responsible for calling incrementCounter / decrementCounter
 * after successful writes / deletes.
 *
 * @param tenantId  Tenant whose counter to check
 * @param uploadBytes  Number of bytes about to be written
 * @param tier  Current tenant tier (determines quota limit)
 * @param db  Drizzle DB handle
 */
export async function checkStorageQuota(
  tenantId: string,
  uploadBytes: number,
  tier: TenantTier,
  db: Db,
): Promise<void> {
  const limit = getStorageQuotaBytes(tier)
  const current = await getCounterValue(db, tenantId, 'storage_bytes', 'all_time')
  if (current + uploadBytes > limit) {
    throw new StorageQuotaExceededError({
      usedBytes: current,
      limitBytes: limit,
      requestedBytes: uploadBytes,
    })
  }
}
