import { Injectable, HttpException, HttpStatus, Logger } from '@nestjs/common';
import { NatsService } from '../../services/nats.service';

interface AgentFileResponse {
  success: boolean;
  error?: string;
  data?: unknown;
}

interface DownloadResult {
  filename: string;
  content: unknown;
}

@Injectable()
export class FilesService {
  private readonly logger = new Logger(FilesService.name);

  constructor(private readonly natsService: NatsService) {}

  // Send a file manager command to the companion agent via NATS request-reply.
  // The agent returns { success: bool, error?: string, data?: VueFinderResponse }.
  private async sendFileCommand(
    licenseId: string,
    payload: Record<string, unknown>,
  ): Promise<unknown> {
    const subject = `corrosion.${licenseId}.files.cmd`;
    try {
      const response = (await this.natsService.request(
        subject,
        payload,
        30000,
      )) as AgentFileResponse | null;

      // Offline mode — agent unreachable, return null rather than crashing
      if (response === null) {
        throw new HttpException(
          'Agent not reachable (offline mode)',
          HttpStatus.SERVICE_UNAVAILABLE,
        );
      }

      if (!response.success) {
        throw new HttpException(
          response.error || 'File operation failed',
          HttpStatus.BAD_REQUEST,
        );
      }

      return response.data;
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logger.error(
        `File command failed on subject ${subject}: ${(error as Error).message}`,
      );
      throw new HttpException(
        'Agent not reachable or timed out',
        HttpStatus.SERVICE_UNAVAILABLE,
      );
    }
  }

  async list(licenseId: string, path: string): Promise<unknown> {
    return this.sendFileCommand(licenseId, { func: 'fm_list', path });
  }

  async search(
    licenseId: string,
    path: string,
    filter: string,
  ): Promise<unknown> {
    return this.sendFileCommand(licenseId, { func: 'fm_search', path, filter });
  }

  async preview(licenseId: string, path: string): Promise<unknown> {
    return this.sendFileCommand(licenseId, { func: 'fm_preview', path });
  }

  async download(licenseId: string, path: string): Promise<DownloadResult> {
    const result = await this.sendFileCommand(licenseId, {
      func: 'fm_preview',
      path,
    });
    const basename = path.split('/').pop() || 'download';
    return { filename: basename, content: result };
  }

  async delete(
    licenseId: string,
    path: string,
    items: string[],
  ): Promise<unknown> {
    return this.sendFileCommand(licenseId, { func: 'fm_delete', path, items });
  }

  async rename(
    licenseId: string,
    path: string,
    item: string,
    name: string,
  ): Promise<unknown> {
    return this.sendFileCommand(licenseId, {
      func: 'fm_rename',
      path,
      items: [item],
      name,
    });
  }

  async move(
    licenseId: string,
    path: string,
    items: string[],
    destination: string,
  ): Promise<unknown> {
    return this.sendFileCommand(licenseId, {
      func: 'fm_move',
      path,
      items,
      destination,
    });
  }

  async copy(
    licenseId: string,
    path: string,
    items: string[],
    destination: string,
  ): Promise<unknown> {
    return this.sendFileCommand(licenseId, {
      func: 'fm_copy',
      path,
      items,
      destination,
    });
  }

  async createFolder(
    licenseId: string,
    path: string,
    name: string,
  ): Promise<unknown> {
    return this.sendFileCommand(licenseId, { func: 'fm_mkdir', path, name });
  }

  async createFile(
    licenseId: string,
    path: string,
    name: string,
  ): Promise<unknown> {
    return this.sendFileCommand(licenseId, { func: 'fm_mkfile', path, name });
  }

  async save(
    licenseId: string,
    path: string,
    content: string,
  ): Promise<unknown> {
    return this.sendFileCommand(licenseId, { func: 'fm_save', path, content });
  }

  async upload(
    licenseId: string,
    path: string,
    file: Express.Multer.File,
  ): Promise<unknown> {
    // Encode binary content as base64 so it survives JSON serialization over NATS
    const content = file.buffer.toString('base64');
    return this.sendFileCommand(licenseId, {
      func: 'fm_upload',
      path,
      filename: file.originalname,
      content,
    });
  }
}
