import { Injectable, Inject, OnModuleDestroy } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Logger } from "nestjs-pino";
import SftpClient from "ssh2-sftp-client";

export interface SftpConfig {
  host: string;
  port: number;
  username: string;
  password: string;
}

@Injectable()
export class SftpClientService implements OnModuleDestroy {
  private client: SftpClient | null = null;

  constructor(
    private readonly configService: ConfigService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  private getConfig(): SftpConfig {
    return {
      host: this.configService.get<string>("SFTP_HOST") || "fs.mvno.net",
      port: this.configService.get<number>("SFTP_PORT") || 22,
      username: this.configService.get<string>("SFTP_USERNAME") || "PASI",
      password: this.configService.get<string>("SFTP_PASSWORD") || "",
    };
  }

  async onModuleDestroy() {
    await this.disconnect();
  }

  private async connect(): Promise<SftpClient> {
    if (this.client) {
      return this.client;
    }

    const config = this.getConfig();
    this.client = new SftpClient();

    try {
      this.logger.log(`Connecting to SFTP: ${config.host}:${config.port}`);
      await this.client.connect({
        host: config.host,
        port: config.port,
        username: config.username,
        password: config.password,
      });
      this.logger.log(`Connected to SFTP: ${config.host}`);
      return this.client;
    } catch (error) {
      this.client = null;
      this.logger.error(`SFTP connection failed`, { error, host: config.host });
      throw error;
    }
  }

  async disconnect(): Promise<void> {
    if (this.client) {
      try {
        await this.client.end();
        this.logger.log("SFTP connection closed");
      } catch (error) {
        this.logger.warn("Error closing SFTP connection", { error });
      } finally {
        this.client = null;
      }
    }
  }

  /**
   * Download a file from SFTP and return its contents as string
   */
  async downloadFileAsString(remotePath: string): Promise<string> {
    const client = await this.connect();

    try {
      this.logger.log(`Downloading file: ${remotePath}`);
      const buffer = await client.get(remotePath);

      if (Buffer.isBuffer(buffer)) {
        const content = buffer.toString("utf-8");
        this.logger.log(`Downloaded file: ${remotePath} (${content.length} bytes)`);
        return content;
      }

      // If it's a stream, convert to string
      if (typeof buffer === "string") {
        return buffer;
      }

      throw new Error(`Unexpected response type from SFTP get: ${typeof buffer}`);
    } catch (error) {
      this.logger.error(`Failed to download file: ${remotePath}`, { error });
      throw error;
    }
  }

  /**
   * List files in a directory
   */
  async listFiles(remotePath: string = "/"): Promise<string[]> {
    const client = await this.connect();

    try {
      this.logger.log(`Listing files in: ${remotePath}`);
      const files = await client.list(remotePath);
      const fileNames = files.map(f => f.name);
      this.logger.log(`Found ${fileNames.length} files in ${remotePath}`);
      return fileNames;
    } catch (error) {
      this.logger.error(`Failed to list files: ${remotePath}`, { error });
      throw error;
    }
  }

  /**
   * Check if a file exists
   */
  async fileExists(remotePath: string): Promise<boolean> {
    const client = await this.connect();

    try {
      const exists = await client.exists(remotePath);
      return exists !== false;
    } catch (error) {
      this.logger.warn(`Error checking file existence: ${remotePath}`, { error });
      return false;
    }
  }

  /**
   * Get the talk detail file for a specific month
   * Format: PASI_talk-detail-YYYYMM.csv
   */
  getTalkDetailFileName(yearMonth: string): string {
    // yearMonth should be YYYYMM format (e.g., "202509")
    return `PASI_talk-detail-${yearMonth}.csv`;
  }

  /**
   * Get the SMS detail file for a specific month
   * Format: PASI_sms-detail-YYYYMM.csv
   */
  getSmsDetailFileName(yearMonth: string): string {
    // yearMonth should be YYYYMM format (e.g., "202509")
    return `PASI_sms-detail-${yearMonth}.csv`;
  }

  /**
   * Get the month string for import (2 months behind current)
   * e.g., If current month is November 2025, returns "202509" (September 2025)
   */
  getAvailableMonth(): string {
    const now = new Date();
    // Go back 2 months
    now.setMonth(now.getMonth() - 2);
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, "0");
    return `${year}${month}`;
  }

  /**
   * Download talk detail CSV for a specific month
   */
  async downloadTalkDetail(yearMonth: string): Promise<string> {
    const fileName = this.getTalkDetailFileName(yearMonth);
    return this.downloadFileAsString(`/${fileName}`);
  }

  /**
   * Download SMS detail CSV for a specific month
   */
  async downloadSmsDetail(yearMonth: string): Promise<string> {
    const fileName = this.getSmsDetailFileName(yearMonth);
    return this.downloadFileAsString(`/${fileName}`);
  }
}

