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

/**
 * 文件上传结果
 */
export interface UploadResult {
  /** 文件唯一标识 */
  key: string;
  /** 公开访问URL */
  url: string;
  /** 文件大小(字节) */
  size: number;
  /** MIME类型 */
  contentType: string;
}

/**
 * MinIO 对象存储服务
 *
 * 用于处理用户头像等文件的上传和管理
 */
@Injectable()
export class StorageService implements OnModuleInit {
  private readonly logger = new Logger(StorageService.name);
  private client: Minio.Client;
  private bucketAvatars: string;
  private publicUrl: string;

  constructor(private readonly configService: ConfigService) {
    const endpoint = this.configService.get<string>(
      'MINIO_ENDPOINT',
      'localhost',
    );
    const port = this.configService.get<number>('MINIO_PORT', 9000);
    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.bucketAvatars = this.configService.get<string>(
      'MINIO_BUCKET_AVATARS',
      'avatars',
    );
    this.publicUrl = this.configService.get<string>(
      'MINIO_PUBLIC_URL',
      'http://localhost:9000',
    );

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

    this.logger.log(`MinIO client initialized: ${endpoint}:${port}`);
  }

  async onModuleInit() {
    // 检查并创建头像存储桶
    await this.ensureBucket(this.bucketAvatars);
  }

  /**
   * 确保存储桶存在
   */
  private async ensureBucket(bucketName: string): Promise<void> {
    try {
      const exists = await this.client.bucketExists(bucketName);
      if (!exists) {
        await this.client.makeBucket(bucketName);
        this.logger.log(`Bucket created: ${bucketName}`);

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

  /**
   * 上传用户头像
   *
   * @param userId 用户ID
   * @param buffer 文件内容
   * @param contentType MIME类型 (image/jpeg, image/png, etc.)
   * @returns 上传结果
   */
  async uploadAvatar(
    userId: string,
    buffer: Buffer,
    contentType: string,
  ): Promise<UploadResult> {
    // 生成唯一文件名: avatars/userId/uuid.ext
    const extension = this.getExtensionFromContentType(contentType);
    const key = `${userId}/${uuidv4()}${extension}`;

    try {
      await this.client.putObject(
        this.bucketAvatars,
        key,
        buffer,
        buffer.length,
        { 'Content-Type': contentType },
      );

      const url = `${this.publicUrl}/${this.bucketAvatars}/${key}`;

      this.logger.log(`Avatar uploaded: ${key} (${buffer.length} bytes)`);

      return {
        key,
        url,
        size: buffer.length,
        contentType,
      };
    } catch (error) {
      this.logger.error(`Failed to upload avatar: ${error.message}`);
      throw new Error(`文件上传失败: ${error.message}`);
    }
  }

  /**
   * 删除用户头像
   *
   * @param key 文件key
   */
  async deleteAvatar(key: string): Promise<void> {
    try {
      await this.client.removeObject(this.bucketAvatars, key);
      this.logger.log(`Avatar deleted: ${key}`);
    } catch (error) {
      this.logger.error(`Failed to delete avatar: ${error.message}`);
      // 删除失败不抛出异常
    }
  }

  /**
   * 从URL中提取文件key
   */
  extractKeyFromUrl(url: string): string | null {
    if (!url || url.startsWith('<svg')) {
      return null; // SVG内容不是URL
    }

    try {
      const urlObj = new URL(url);
      // URL格式: http://host/bucket/key
      const pathParts = urlObj.pathname.split('/').filter((p) => p);
      if (pathParts.length >= 2 && pathParts[0] === this.bucketAvatars) {
        return pathParts.slice(1).join('/');
      }
    } catch {
      // 不是有效URL
    }
    return null;
  }

  /**
   * 根据Content-Type获取文件扩展名
   */
  private getExtensionFromContentType(contentType: string): string {
    const map: Record<string, string> = {
      'image/jpeg': '.jpg',
      'image/jpg': '.jpg',
      'image/png': '.png',
      'image/gif': '.gif',
      'image/webp': '.webp',
    };
    return map[contentType] || '.jpg';
  }

  /**
   * 验证图片类型
   */
  isValidImageType(contentType: string): boolean {
    const validTypes = [
      'image/jpeg',
      'image/jpg',
      'image/png',
      'image/gif',
      'image/webp',
    ];
    return validTypes.includes(contentType);
  }

  /**
   * 最大头像文件大小 (5MB)
   */
  get maxAvatarSize(): number {
    return 5 * 1024 * 1024;
  }

  /**
   * 最大通用图片文件大小 (10MB)
   */
  get maxImageSize(): number {
    return 10 * 1024 * 1024;
  }

  /**
   * 上传通用图片
   *
   * 用于办公室照片等通用图片上传
   *
   * @param userId 用户ID
   * @param category 分类 (如 'office-photos')
   * @param buffer 文件内容
   * @param contentType MIME类型 (image/jpeg, image/png, etc.)
   * @returns 上传结果
   */
  async uploadImage(
    userId: string,
    category: string,
    buffer: Buffer,
    contentType: string,
  ): Promise<UploadResult> {
    // 使用 avatars bucket 存储所有用户图片
    // 路径格式: userId/category/uuid.ext
    const extension = this.getExtensionFromContentType(contentType);
    const key = `${userId}/${category}/${uuidv4()}${extension}`;

    try {
      await this.client.putObject(
        this.bucketAvatars,
        key,
        buffer,
        buffer.length,
        { 'Content-Type': contentType },
      );

      const url = `${this.publicUrl}/${this.bucketAvatars}/${key}`;

      this.logger.log(`Image uploaded: ${key} (${buffer.length} bytes)`);

      return {
        key,
        url,
        size: buffer.length,
        contentType,
      };
    } catch (error) {
      this.logger.error(`Failed to upload image: ${error.message}`);
      throw new Error(`文件上传失败: ${error.message}`);
    }
  }
}
