/**
 * File Writer
 * Handles writing generated files to disk with retry logic
 */

import * as fs from 'fs';
import * as path from 'path';
import { GeneratedFile, ProgressCallback } from './types';
import { RetryHelpers } from '../utils/retry-manager';

export interface WriteOptions {
  dryRun?: boolean; // If true, don't actually write files
  verbose?: boolean; // If true, log what's being written
  onProgress?: ProgressCallback; // Progress callback
}

export interface WriteResult {
  written: string[];
  skipped: string[];
  errors: Array<{ path: string; error: string }>;
}

export class FileWriter {
  /**
   * Write generated files to disk
   */
  async write(files: GeneratedFile[], options: WriteOptions = {}): Promise<WriteResult> {
    const result: WriteResult = {
      written: [],
      skipped: [],
      errors: [],
    };

    const total = files.length;
    let current = 0;

    for (const file of files) {
      try {
        // Report progress before writing
        if (options.onProgress) {
          options.onProgress({
            stage: 'Writing Files',
            current,
            total,
            file: file.path,
            message: `Writing ${path.basename(file.path)}`,
          });
        }

        await this.writeFile(file, options, result);
        current++;

        // Report progress after writing
        if (options.onProgress) {
          options.onProgress({
            stage: 'Writing Files',
            current,
            total,
            file: file.path,
            message: `Wrote ${path.basename(file.path)}`,
          });
        }
      } catch (error) {
        result.errors.push({
          path: file.path,
          error: error instanceof Error ? error.message : String(error),
        });
        current++;
      }
    }

    return result;
  }

  /**
   * Write a single file with retry logic
   */
  private async writeFile(
    file: GeneratedFile,
    options: WriteOptions,
    result: WriteResult
  ): Promise<void> {
    const absolutePath = path.resolve(file.path);

    // Check if file exists
    const exists = await RetryHelpers.retryFileOperation(
      async () => fs.existsSync(absolutePath),
      'check file existence'
    );

    // Skip if file exists and overwrite is false
    if (exists && file.overwrite === false) {
      if (options.verbose) {
        console.log(`Skipping ${file.path} (file exists, overwrite=false)`);
      }
      result.skipped.push(file.path);
      return;
    }

    // Handle preserve mode (future enhancement)
    if (exists && file.preserve) {
      // TODO: Implement smart merging
      // For now, just skip
      if (options.verbose) {
        console.log(`Skipping ${file.path} (preserve mode)`);
      }
      result.skipped.push(file.path);
      return;
    }

    // Dry run mode
    if (options.dryRun) {
      if (options.verbose) {
        console.log(`Would write ${file.path} (dry run)`);
      }
      result.written.push(file.path);
      return;
    }

    // Ensure directory exists with retry
    const dir = path.dirname(absolutePath);
    await RetryHelpers.retryFileOperation(
      async () => {
        if (!fs.existsSync(dir)) {
          fs.mkdirSync(dir, { recursive: true });
        }
      },
      `create directory ${dir}`
    );

    // Write file with retry
    await RetryHelpers.retryFileOperation(
      async () => {
        fs.writeFileSync(absolutePath, file.content, 'utf-8');
      },
      `write file ${path.basename(file.path)}`
    );

    if (options.verbose) {
      console.log(`Wrote ${file.path}`);
    }

    result.written.push(file.path);
  }

  /**
   * Delete a file with retry logic
   */
  async delete(filePath: string, options: WriteOptions = {}): Promise<boolean> {
    const absolutePath = path.resolve(filePath);

    const exists = await RetryHelpers.retryFileOperation(
      async () => fs.existsSync(absolutePath),
      'check file existence'
    );

    if (!exists) {
      return false;
    }

    if (options.dryRun) {
      if (options.verbose) {
        console.log(`Would delete ${filePath} (dry run)`);
      }
      return true;
    }

    await RetryHelpers.retryFileOperation(
      async () => {
        fs.unlinkSync(absolutePath);
      },
      `delete file ${path.basename(filePath)}`
    );

    if (options.verbose) {
      console.log(`Deleted ${filePath}`);
    }

    return true;
  }

  /**
   * Check if a file exists with retry logic
   */
  async exists(filePath: string): Promise<boolean> {
    return RetryHelpers.retryFileOperation(
      async () => fs.existsSync(path.resolve(filePath)),
      'check file existence'
    );
  }

  /**
   * Read a file with retry logic
   */
  async read(filePath: string): Promise<string> {
    return RetryHelpers.retryFileOperation(
      async () => fs.readFileSync(path.resolve(filePath), 'utf-8'),
      `read file ${path.basename(filePath)}`
    );
  }
}
