import { createReadStream, createWriteStream, existsSync, promises as fs, statSync } from 'fs';
import { basename, dirname } from 'path';
import { createGzip } from 'zlib';
import { pipeline } from 'stream/promises';
import type { AuditConfig } from './types.js';

export class AuditRotator {
  private config: AuditConfig;
  private lastRotationCheck = 0;
  private readonly CHECK_INTERVAL = 60_000; // Check every minute

  constructor(config: AuditConfig) {
    this.config = config;
  }

  checkRotation(): boolean {
    const now = Date.now();
    if (now - this.lastRotationCheck < this.CHECK_INTERVAL) {
      return false;
    }
    this.lastRotationCheck = now;

    return this.rotateIfNeeded();
  }

  private rotateIfNeeded(): boolean {
    if (!existsSync(this.config.path)) {
      return false;
    }

    const stats = statSync(this.config.path);
    const sizeMb = stats.size / (1024 * 1024);

    if (sizeMb < this.config.max_size_mb) {
      return false;
    }

    this.rotate();
    return true;
  }

  private rotate(): void {
    const basePath = this.config.path;
    const baseName = basename(basePath);
    const dir = dirname(basePath);
    const timestamp = new Date().toISOString().split('T')[0];
    const rotatedPath = `${dir}/${baseName}.${timestamp}`;
    const compressedPath = `${rotatedPath}.gz`;

    try {
      // Move current log to rotated name
      if (existsSync(rotatedPath)) {
        fs.unlink(rotatedPath);
      }
      if (existsSync(compressedPath)) {
        fs.unlink(compressedPath);
      }

      fs.rename(basePath, rotatedPath);

      // Compress the rotated file
      const gzip = createGzip();
      const input = createReadStream(rotatedPath);
      const output = createWriteStream(compressedPath);

      pipeline(input, gzip, output).then(() => {
        fs.unlink(rotatedPath);
      }).catch((err) => {
        console.error('Audit log compression failed:', err);
      });

      // Clean up old logs
      this.cleanupOldLogs();
    } catch (err) {
      console.error('Audit log rotation failed:', err);
    }
  }

  private async cleanupOldLogs(): Promise<void> {
    const dir = dirname(this.config.path);
    const baseName = basename(this.config.path);
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - this.config.keep_days);

    try {
      const files = await fs.readdir(dir);

      for (const file of files) {
        if (!file.startsWith(baseName)) {continue;}

        const filePath = `${dir}/${file}`;
        const stats = await fs.stat(filePath);
        const fileDate = new Date(stats.mtime);

        if (fileDate < cutoffDate) {
          await fs.unlink(filePath);
        }
      }
    } catch (err) {
      console.error('Audit log cleanup failed:', err);
    }
  }

  async forceRotation(): Promise<void> {
    this.rotate();
  }
}
