/**
 * Audit Logger
 * Logs content security audit events with content desensitization.
 * Logs are retained for at least 180 days.
 *
 * Features:
 * - Content desensitization: only first 3 and last 3 chars kept, middle replaced with ***
 * - Access Token never logged in plaintext
 * - Structured JSON log format
 * - Automatic log rotation
 */

const fs = require('fs');
const path = require('path');

// ============================================================
// Configuration
// ============================================================
const LOG_DIR = path.join(__dirname, '..', 'logs', 'audit');
const LOG_RETENTION_DAYS = 180;
const LOG_ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1000; // Rotate daily

class AuditLogger {
  constructor() {
    this._ensureLogDir();
    this._currentLogFile = this._getLogFilePath(new Date());
    this._rotationTimer = null;
    this._startRotation();
  }

  /**
   * Log an audit event.
   * @param {object} entry
   * @param {string} entry.userId - User identifier (openid)
   * @param {string} entry.contentType - 'text' or 'image'
   * @param {string} entry.contentSummary - Desensitized content summary
   * @param {number} entry.scene - Scene value
   * @param {string} entry.result - 'pass', 'reject', 'error', 'rejected'
   * @param {string} entry.reason - Reason for the result
   * @param {number} [entry.duration] - Duration of the check in ms
   */
  logAudit(entry) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      userId: entry.userId || 'unknown',
      contentType: entry.contentType || 'unknown',
      contentSummary: this._sanitizeContent(entry.contentSummary),
      scene: entry.scene || 0,
      result: entry.result || 'unknown',
      reason: entry.reason || '',
      duration: entry.duration || 0,
    };

    const logLine = JSON.stringify(logEntry) + '\n';

    try {
      fs.appendFileSync(this._currentLogFile, logLine, 'utf8');
    } catch (err) {
      console.error('[AuditLogger] Failed to write log:', err.message);
    }
  }

  /**
   * Log a violation event.
   * @param {object} entry
   * @param {string} entry.userId - User identifier
   * @param {string} entry.violationType - Type of violation
   * @param {string} entry.contentSummary - Desensitized content
   * @param {string} entry.action - Action taken (e.g., 'mute_24h')
   */
  logViolation(entry) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      type: 'violation',
      userId: entry.userId || 'unknown',
      violationType: entry.violationType || 'unknown',
      contentSummary: this._sanitizeContent(entry.contentSummary),
      action: entry.action || '',
    };

    const logLine = JSON.stringify(logEntry) + '\n';

    try {
      fs.appendFileSync(this._currentLogFile, logLine, 'utf8');
    } catch (err) {
      console.error('[AuditLogger] Failed to write violation log:', err.message);
    }
  }

  /**
   * Log a report event.
   * @param {object} entry
   * @param {string} entry.reporterId - Reporter's user ID (kept confidential)
   * @param {string} entry.targetUserId - Reported user's ID
   * @param {string} entry.contentSummary - Desensitized reported content
   * @param {string} entry.reason - Report reason
   */
  logReport(entry) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      type: 'report',
      reporterId: entry.reporterId ? '***' : 'unknown', // Keep reporter confidential
      targetUserId: entry.targetUserId || 'unknown',
      contentSummary: this._sanitizeContent(entry.contentSummary),
      reason: entry.reason || '',
    };

    const logLine = JSON.stringify(logEntry) + '\n';

    try {
      fs.appendFileSync(this._currentLogFile, logLine, 'utf8');
    } catch (err) {
      console.error('[AuditLogger] Failed to write report log:', err.message);
    }
  }

  /**
   * Ensure the log directory exists.
   */
  _ensureLogDir() {
    try {
      if (!fs.existsSync(LOG_DIR)) {
        fs.mkdirSync(LOG_DIR, { recursive: true });
      }
    } catch (err) {
      console.error('[AuditLogger] Failed to create log directory:', err.message);
    }
  }

  /**
   * Get the log file path for a given date.
   * @param {Date} date
   * @returns {string}
   */
  _getLogFilePath(date) {
    const dateStr = date.toISOString().split('T')[0]; // YYYY-MM-DD
    return path.join(LOG_DIR, `audit-${dateStr}.log`);
  }

  /**
   * Start daily log rotation.
   */
  _startRotation() {
    this._rotationTimer = setInterval(() => {
      const newLogFile = this._getLogFilePath(new Date());
      if (newLogFile !== this._currentLogFile) {
        this._currentLogFile = newLogFile;
        console.log('[AuditLogger] Rotated to new log file:', newLogFile);
      }
      // Clean up old logs
      this._cleanOldLogs();
    }, LOG_ROTATION_INTERVAL_MS);
  }

  /**
   * Remove log files older than the retention period.
   */
  _cleanOldLogs() {
    try {
      const files = fs.readdirSync(LOG_DIR);
      const cutoff = Date.now() - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000;

      for (const file of files) {
        const filePath = path.join(LOG_DIR, file);
        const stat = fs.statSync(filePath);
        if (stat.birthtimeMs < cutoff) {
          fs.unlinkSync(filePath);
          console.log('[AuditLogger] Deleted old log file:', file);
        }
      }
    } catch (err) {
      console.error('[AuditLogger] Failed to clean old logs:', err.message);
    }
  }

  /**
   * Sanitize content: keep only first 3 and last 3 chars, replace middle with ***.
   * @param {string} content
   * @returns {string}
   */
  _sanitizeContent(content) {
    if (!content || typeof content !== 'string') return '';
    if (content.length <= 6) return content;
    return content.substring(0, 3) + '***' + content.substring(content.length - 3);
  }

  /**
   * Clean up resources.
   */
  destroy() {
    if (this._rotationTimer) {
      clearInterval(this._rotationTimer);
      this._rotationTimer = null;
    }
    console.log('[AuditLogger] Destroyed');
  }
}

module.exports = AuditLogger;