import type { MemoryStorageOptions, StorageProvider } from '../types';

export declare class MemoryStorage implements StorageProvider {
  private records: Map<string, { count: number, resetTime: number }>
  private timestamps: Map<string, number[]>
  private cleanupTimer: NodeJS.Timeout | null
  private enableAutoCleanup: boolean
  private cleanupIntervalMs: number

  constructor(options?: MemoryStorageOptions) {
    this.records = new Map()
    this.timestamps = new Map()

    const defaultConfig = config.memoryStorage || {}

    this.enableAutoCleanup = options?.enableAutoCleanup ?? defaultConfig.enableAutoCleanup ?? false
    this.cleanupIntervalMs = options?.cleanupIntervalMs ?? defaultConfig.cleanupIntervalMs ?? 60 * 1000
    this.cleanupTimer = null

    if (this.enableAutoCleanup) {
      this.startCleanupTimer()
    }
  }

  async increment(key: string, windowMs: number): Promise<{ count: number, resetTime: number }> {
    const now = Date.now()
    const record = this.records.get(key)

    if (!record || now > record.resetTime) {
      const newRecord = {
        count: 1,
        resetTime: now + windowMs,
      }
      this.records.set(key, newRecord)
      this.timestamps.set(key, [now])
      return newRecord
    }

    record.count += 1

    const timestamps = this.timestamps.get(key) || []
    timestamps.push(now)
    this.timestamps.set(key, timestamps)

    return record
  }

  async reset(key: string): Promise<void> {
    this.records.delete(key)
    this.timestamps.delete(key)
  }

  async getCount(key: string): Promise<number> {
    const record = this.records.get(key)
    return record?.count || 0
  }

  async getSlidingWindowCount(key: string, windowMs: number): Promise<number> {
    const timestamps = this.timestamps.get(key) || []
    const now = Date.now()
    const windowStart = now - windowMs

    return timestamps.filter(time => time > windowStart).length
  }

  async batchIncrement(keys: string[], windowMs: number): Promise<Map<string, { count: number, resetTime: number }>> {
    const results = new Map<string, { count: number, resetTime: number }>()

    for (const key of keys) {
      const result = await this.increment(key, windowMs)
      results.set(key, result)
    }

    return results
  }

  cleanExpired(): void {
    const now = Date.now()

    for (const [key, record] of this.records.entries()) {
      if (now > record.resetTime) {
        this.records.delete(key)
      }
    }

    const maxAge = now - 3600000
    for (const [key, timestamps] of this.timestamps.entries()) {
      const filtered = timestamps.filter(time => time > maxAge)
      if (filtered.length === 0) {
        this.timestamps.delete(key)
      }
      else if (filtered.length !== timestamps.length) {
        this.timestamps.set(key, filtered)
      }
    }
  }

  dispose(): void {
    if (this.cleanupTimer) {
      clearInterval(this.cleanupTimer)
    }
  }

  private startCleanupTimer(): void {
    this.cleanupTimer = setInterval(() => this.cleanExpired(), this.cleanupIntervalMs)
  }
}