import * as plugins from './mod.plugins.js';
import * as paths from '../paths.js';

export interface IFileCache {
  path: string;
  checksum: string;
  modified: number;
  size: number;
}

export interface ICacheManifest {
  version: string;
  lastFormat: number;
  files: IFileCache[];
}

export class ChangeCache {
  private cacheDir: string;
  private manifestPath: string;
  private cacheVersion = '1.0.0';
  
  constructor() {
    this.cacheDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-cache');
    this.manifestPath = plugins.path.join(this.cacheDir, 'manifest.json');
  }
  
  async initialize(): Promise<void> {
    await plugins.smartfile.fs.ensureDir(this.cacheDir);
  }
  
  async getManifest(): Promise<ICacheManifest> {
    const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
    if (!exists) {
      return {
        version: this.cacheVersion,
        lastFormat: 0,
        files: []
      };
    }
    
    const content = await plugins.smartfile.fs.toStringSync(this.manifestPath);
    return JSON.parse(content);
  }
  
  async saveManifest(manifest: ICacheManifest): Promise<void> {
    await plugins.smartfile.memory.toFs(JSON.stringify(manifest, null, 2), this.manifestPath);
  }
  
  async hasFileChanged(filePath: string): Promise<boolean> {
    const absolutePath = plugins.path.isAbsolute(filePath) 
      ? filePath 
      : plugins.path.join(paths.cwd, filePath);
    
    // Check if file exists
    const exists = await plugins.smartfile.fs.fileExists(absolutePath);
    if (!exists) {
      return true; // File doesn't exist, so it's "changed" (will be created)
    }
    
    // Get current file stats
    const stats = await plugins.smartfile.fs.stat(absolutePath);
    const content = await plugins.smartfile.fs.toStringSync(absolutePath);
    const currentChecksum = this.calculateChecksum(content);
    
    // Get cached info
    const manifest = await this.getManifest();
    const cachedFile = manifest.files.find(f => f.path === filePath);
    
    if (!cachedFile) {
      return true; // Not in cache, so it's changed
    }
    
    // Compare checksums
    return cachedFile.checksum !== currentChecksum || 
           cachedFile.size !== stats.size ||
           cachedFile.modified !== stats.mtimeMs;
  }
  
  async updateFileCache(filePath: string): Promise<void> {
    const absolutePath = plugins.path.isAbsolute(filePath) 
      ? filePath 
      : plugins.path.join(paths.cwd, filePath);
    
    // Get current file stats
    const stats = await plugins.smartfile.fs.stat(absolutePath);
    const content = await plugins.smartfile.fs.toStringSync(absolutePath);
    const checksum = this.calculateChecksum(content);
    
    // Update manifest
    const manifest = await this.getManifest();
    const existingIndex = manifest.files.findIndex(f => f.path === filePath);
    
    const cacheEntry: IFileCache = {
      path: filePath,
      checksum,
      modified: stats.mtimeMs,
      size: stats.size
    };
    
    if (existingIndex !== -1) {
      manifest.files[existingIndex] = cacheEntry;
    } else {
      manifest.files.push(cacheEntry);
    }
    
    manifest.lastFormat = Date.now();
    await this.saveManifest(manifest);
  }
  
  async getChangedFiles(filePaths: string[]): Promise<string[]> {
    const changedFiles: string[] = [];
    
    for (const filePath of filePaths) {
      if (await this.hasFileChanged(filePath)) {
        changedFiles.push(filePath);
      }
    }
    
    return changedFiles;
  }
  
  async clean(): Promise<void> {
    const manifest = await this.getManifest();
    const validFiles: IFileCache[] = [];
    
    // Remove entries for files that no longer exist
    for (const file of manifest.files) {
      const absolutePath = plugins.path.isAbsolute(file.path) 
        ? file.path 
        : plugins.path.join(paths.cwd, file.path);
      
      if (await plugins.smartfile.fs.fileExists(absolutePath)) {
        validFiles.push(file);
      }
    }
    
    manifest.files = validFiles;
    await this.saveManifest(manifest);
  }
  
  private calculateChecksum(content: string | Buffer): string {
    return plugins.crypto.createHash('sha256').update(content).digest('hex');
  }
}