import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as Minio from 'minio';
import * as crypto from 'crypto';

/**
 * MinIO 存储服务
 *
 * 负责上传合同签名图片和已签署的 PDF 文件到 MinIO 对象存储
 */
@Injectable()
export class MinioStorageService implements OnModuleInit {
  private readonly logger = new Logger(MinioStorageService.name);
  private minioClient: Minio.Client;
  private readonly bucketName: string;
  private readonly publicUrl: string;

  constructor(private readonly configService: ConfigService) {
    const endpoint = this.configService.get<string>('MINIO_ENDPOINT', 'localhost');
    const port = parseInt(this.configService.get<string>('MINIO_PORT', '9000'), 10);
    const useSSL = this.configService.get<string>('MINIO_USE_SSL', 'false') === 'true';
    const accessKey = this.configService.get<string>('MINIO_ACCESS_KEY', 'admin');
    const secretKey = this.configService.get<string>('MINIO_SECRET_KEY', 'minio_secret_password');

    this.bucketName = this.configService.get<string>('MINIO_BUCKET_CONTRACTS', 'contracts');
    this.publicUrl = this.configService.get<string>('MINIO_PUBLIC_URL', `http://${endpoint}:${port}`);

    this.minioClient = new Minio.Client({
      endPoint: endpoint,
      port: port,
      useSSL: useSSL,
      accessKey: accessKey,
      secretKey: secretKey,
    });

    this.logger.log(`MinIO client configured: ${endpoint}:${port}, bucket: ${this.bucketName}`);
  }

  async onModuleInit() {
    await this.ensureBucketExists();
  }

  /**
   * 确保存储桶存在
   */
  private async ensureBucketExists(): Promise<void> {
    try {
      const exists = await this.minioClient.bucketExists(this.bucketName);
      if (!exists) {
        await this.minioClient.makeBucket(this.bucketName, 'cn-east-1');
        this.logger.log(`Created bucket: ${this.bucketName}`);

        // 设置桶策略为公开读取
        const policy = {
          Version: '2012-10-17',
          Statement: [
            {
              Effect: 'Allow',
              Principal: { AWS: ['*'] },
              Action: ['s3:GetObject'],
              Resource: [`arn:aws:s3:::${this.bucketName}/*`],
            },
          ],
        };
        await this.minioClient.setBucketPolicy(this.bucketName, JSON.stringify(policy));
        this.logger.log(`Set public read policy for bucket: ${this.bucketName}`);
      } else {
        this.logger.log(`Bucket exists: ${this.bucketName}`);
      }
    } catch (error) {
      this.logger.error(`Failed to ensure bucket exists: ${error.message}`);
      // 不抛出错误，允许服务继续启动（可能 MinIO 暂时不可用）
    }
  }

  /**
   * 上传签名图片
   * @param orderNo 订单号
   * @param signatureBuffer 签名图片 Buffer (PNG)
   * @returns 签名图片的公开 URL
   */
  async uploadSignature(orderNo: string, signatureBuffer: Buffer): Promise<string> {
    const timestamp = Date.now();
    const hash = crypto.createHash('md5').update(signatureBuffer).digest('hex').slice(0, 8);
    const objectName = `signatures/${orderNo}/${timestamp}-${hash}.png`;

    try {
      await this.minioClient.putObject(
        this.bucketName,
        objectName,
        signatureBuffer,
        signatureBuffer.length,
        {
          'Content-Type': 'image/png',
          'x-amz-acl': 'public-read',
        },
      );

      const url = `${this.publicUrl}/${this.bucketName}/${objectName}`;
      this.logger.log(`Uploaded signature for order ${orderNo}: ${url}`);
      return url;
    } catch (error) {
      this.logger.error(`Failed to upload signature for order ${orderNo}: ${error.message}`);
      throw new Error(`签名图片上传失败: ${error.message}`);
    }
  }

  /**
   * 上传已签署的合同 PDF
   * @param orderNo 订单号
   * @param pdfBuffer PDF 文件 Buffer
   * @returns PDF 文件的公开 URL
   */
  async uploadSignedPdf(orderNo: string, pdfBuffer: Buffer): Promise<string> {
    const timestamp = Date.now();
    const objectName = `contracts/${orderNo}/signed-contract-${timestamp}.pdf`;

    try {
      await this.minioClient.putObject(
        this.bucketName,
        objectName,
        pdfBuffer,
        pdfBuffer.length,
        {
          'Content-Type': 'application/pdf',
          'x-amz-acl': 'public-read',
          'Content-Disposition': `inline; filename="contract-${orderNo}.pdf"`,
        },
      );

      const url = `${this.publicUrl}/${this.bucketName}/${objectName}`;
      this.logger.log(`Uploaded signed PDF for order ${orderNo}: ${url}`);
      return url;
    } catch (error) {
      this.logger.error(`Failed to upload signed PDF for order ${orderNo}: ${error.message}`);
      throw new Error(`已签署合同上传失败: ${error.message}`);
    }
  }

  /**
   * 下载已签署的合同 PDF
   * @param signedPdfUrl PDF 文件的公开 URL
   * @returns PDF 文件 Buffer
   */
  async downloadSignedPdf(signedPdfUrl: string): Promise<Buffer> {
    try {
      // 从 URL 中提取对象名称
      const objectName = this.extractObjectName(signedPdfUrl);

      const dataStream = await this.minioClient.getObject(this.bucketName, objectName);

      // 将流转换为 Buffer
      const chunks: Buffer[] = [];
      for await (const chunk of dataStream) {
        chunks.push(Buffer.from(chunk));
      }

      const buffer = Buffer.concat(chunks);
      this.logger.log(`Downloaded signed PDF: ${objectName}, size: ${buffer.length} bytes`);
      return buffer;
    } catch (error) {
      this.logger.error(`Failed to download signed PDF from ${signedPdfUrl}: ${error.message}`);
      throw new Error(`已签署合同下载失败: ${error.message}`);
    }
  }

  /**
   * 从 URL 中提取对象名称
   */
  private extractObjectName(objectUrl: string): string {
    // URL 格式: https://minio.xxx.com/contracts/contracts/orderNo/signed-contract-xxx.pdf
    // 需要提取: contracts/orderNo/signed-contract-xxx.pdf
    const url = new URL(objectUrl);
    const pathParts = url.pathname.split('/').filter(Boolean);
    // 第一个是桶名，后面的是对象路径
    return pathParts.slice(1).join('/');
  }

  /**
   * 删除文件
   * @param objectUrl 文件的公开 URL
   */
  async deleteObject(objectUrl: string): Promise<void> {
    try {
      // 从 URL 中提取对象名称
      const urlParts = objectUrl.replace(this.publicUrl, '').split('/');
      const objectName = urlParts.slice(2).join('/'); // 去掉空字符串和桶名

      await this.minioClient.removeObject(this.bucketName, objectName);
      this.logger.log(`Deleted object: ${objectName}`);
    } catch (error) {
      this.logger.error(`Failed to delete object: ${error.message}`);
      // 删除失败不抛出错误
    }
  }
}
