
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';

export class FileLogger {
  private logDir: string;
  private logFile: string;
  private maxFileSize: number;
  private maxFiles: number;

  constructor(
    logName: string = 'ludus-mcp', 
    maxFileSize: number = 10 * 1024 * 1024, // 10MB
    maxFiles: number = 5
  ) {
    // Create logs directory in user's temp/home directory
    this.logDir = path.join(os.homedir(), '.ludus-mcp', 'logs');
    this.logFile = path.join(this.logDir, `${logName}.log`);
    this.maxFileSize = maxFileSize;
    this.maxFiles = maxFiles;

    this.ensureLogDirectory();
  }

  private ensureLogDirectory(): void {
    try {
      if (!fs.existsSync(this.logDir)) {
        fs.mkdirSync(this.logDir, { recursive: true });
      }
    } catch (error) {
      console.error('Failed to create log directory:', error);
    }
  }

  private rotateIfNeeded(): void {
    try {
      if (!fs.existsSync(this.logFile)) {
        return;
      }

      const stats = fs.statSync(this.logFile);
      if (stats.size >= this.maxFileSize) {
        this.rotateFiles();
      }
    } catch (error) {
      console.error('Error checking log file size:', error);
    }
  }

  private rotateFiles(): void {
    try {
      // Move current log to .1, .1 to .2, etc.
      for (let i = this.maxFiles - 1; i >= 1; i--) {
        const oldFile = `${this.logFile}.${i}`;
        const newFile = `${this.logFile}.${i + 1}`;
        
        if (fs.existsSync(oldFile)) {
          if (i === this.maxFiles - 1) {
            fs.unlinkSync(oldFile); // Delete oldest
          } else {
            fs.renameSync(oldFile, newFile);
          }
        }
      }

      // Move current log to .1
      if (fs.existsSync(this.logFile)) {
        fs.renameSync(this.logFile, `${this.logFile}.1`);
      }
    } catch (error) {
      console.error('Error rotating log files:', error);
    }
  }

  private formatValue(value: any): string {
    if (value === null) return 'null';
    if (value === undefined) return 'undefined';
    if (typeof value === 'string') return value;
    if (typeof value === 'number' || typeof value === 'boolean') return String(value);
    
    // Handle Error objects specially
    if (value instanceof Error) {
      return `${value.name}: ${value.message}\n${value.stack || 'No stack trace'}`;
    }
    
    // Handle objects that might have circular references or non-enumerable properties
    try {
      // First try regular JSON.stringify
      return JSON.stringify(value, null, 2);
    } catch (jsonError) {
      // Fallback: try to extract common error properties manually
      if (value && typeof value === 'object') {
        const errorProps: string[] = [];
        
        // Try to get common error properties
        const commonProps = ['message', 'code', 'errno', 'syscall', 'path', 'name', 'stack', 'signal', 'status', 'stderr', 'stdout'];
        for (const prop of commonProps) {
          if (prop in value && value[prop] !== undefined) {
            errorProps.push(`${prop}: ${String(value[prop])}`);
          }
        }
        
        // If we found some properties, use them
        if (errorProps.length > 0) {
          return errorProps.join('\n');
        }
        
        // Last resort: try to get all enumerable properties
        try {
          const props = Object.keys(value).map(key => `${key}: ${String(value[key])}`);
          if (props.length > 0) {
            return props.join('\n');
          }
        } catch (e) {
          // Even this failed
        }
      }
      
      // Final fallback
      return `[Complex object - toString: ${String(value)}]`;
    }
  }

  log(level: string, context: string, message: string, meta?: Record<string, any>): void {
    try {
      this.rotateIfNeeded();

      const timestamp = new Date().toISOString();
      let logLine = `[${timestamp}] ${level.toUpperCase()} [${context}] ${message}`;
      
      if (meta && Object.keys(meta).length > 0) {
        logLine += '\n  Metadata:';
        for (const [key, value] of Object.entries(meta)) {
          const formattedValue = this.formatValue(value);
          logLine += `\n    ${key}: ${formattedValue}`;
        }
      }
      
      logLine += '\n\n';
      
      fs.appendFileSync(this.logFile, logLine, 'utf8');
    } catch (error) {
      console.error('Failed to write to log file:', error);
    }
  }

  getLogPath(): string {
    return this.logFile;
  }

  getLogDirectory(): string {
    return this.logDir;
  }
} 