import type { Querier, Schema, TransactionalDatabase } from '@platform-modules/db'

export type MeterKey = string

export type SubjectRef = {
  tenantId: string
  account: string
}

export type Period = {
  id: string
  startsAt: Date
  endsAt: Date
}

export type Overage = 'deny' | 'allow'

export type EntitlementPolicy = {
  meter: MeterKey
  includedUnits: bigint
  hardLimit?: bigint
  overage: Overage
  period: Period
  version: string
}

export type Reservation = {
  id: string
  idempotencyKey: string
  subject: SubjectRef
  meter: MeterKey
  reservedUnits: bigint
  committedUnits?: bigint
  status: 'reserved' | 'committed' | 'released' | 'expired'
  period: Period
  appliedPolicy: {
    includedUnits: bigint
    hardLimit?: bigint
    overage: Overage
    version: string
  }
  expiresAt: Date
}

export type CommitResult = {
  reservation: Reservation
  committedUnits: bigint
  releasedUnits: bigint
}

export type UsageQuery =
  | { subject: SubjectRef; meter: MeterKey; at: Date }
  | { subject: SubjectRef; meter: MeterKey; periodId: string }

export type UsageView = {
  period: Period
  committed: bigint
  reserved: bigint
  includedRemaining: bigint
  capacityRemaining: bigint | null
}

export type Clock = () => Date

export interface PolicyStore {
  resolve(subject: SubjectRef, meter: MeterKey, at: Date): Promise<EntitlementPolicy | null>
}

export interface RatingStore {
  rule(meter: MeterKey, at: Date): Promise<RatingRule | null>
}

export type RatingRule = {
  meter: MeterKey
  ruleVersion: string
  effectiveFrom: Date
  numerator: bigint
  denominator: bigint
}

export type MeteringLedgerUsage = {
  reservationId: string
  subject: SubjectRef
  meter: MeterKey
  units: bigint
  ruleVersion?: string
}

export interface MeteringLedgerPort {
  post(usage: MeteringLedgerUsage): Promise<void>
}

export type MeteringQuerier<S extends Schema = Record<string, never>> = Querier<S>

export type MeteringDatabase<S extends Schema = Record<string, never>> = TransactionalDatabase<S>

export interface MeteringService {
  resolve(subject: SubjectRef, meter: MeterKey, at: Date): Promise<EntitlementPolicy | null>

  reserve(input: {
    subject: SubjectRef
    meter: MeterKey
    units: bigint
    idempotencyKey: string
    expiresAt: Date
  }): Promise<Reservation>

  commit(reservationId: string, actualUnits: bigint): Promise<CommitResult>

  release(reservationId: string): Promise<void>

  expireStale(before: Date, limit: number): Promise<number>

  usage(query: UsageQuery): Promise<UsageView>
}
