import { Injectable, BadRequestException, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import COS from 'cos-nodejs-sdk-v5';
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
import { randomBytes } from 'crypto';
import axios from 'axios';
import AdmZip from 'adm-zip';

@Injectable()
export class OssService {
  private readonly logger = new Logger(OssService.name);
  private client: COS | null = null;
  private readonly bucket: string;
  private readonly region: string;
  private readonly enabled: boolean;

  constructor(private configService: ConfigService) {
    const secretId = this.configService.get<string>('COS_SECRET_ID');
    const secretKey = this.configService.get<string>('COS_SECRET_KEY');
    this.bucket = this.configService.get<string>('COS_BUCKET') || '';
    this.region = this.configService.get<string>('COS_REGION') || 'ap-guangzhou';

    // 检查是否配置了 COS
    this.enabled = !!(secretId && secretKey && this.bucket);

    if (this.enabled) {
      this.client = new COS({
        SecretId: secretId,
        SecretKey: secretKey,
      });
      console.log('腾讯云 COS 已启用，Bucket:', this.bucket, 'Region:', this.region);
    } else {
      console.log('腾讯云 COS 未配置，将使用本地存储');
    }
  }

  /**
   * 检查 COS 是否启用
   */
  isEnabled(): boolean {
    return this.enabled;
  }

  /**
   * 上传文件到 COS
   * @param file 文件 Buffer 或 Stream
   * @param originalName 原始文件名
   * @param tenantId 租户ID（可选，用于目录隔离）
   * @param userId 用户ID（可选，用于目录隔离）
   * @returns 文件访问URL
   */
  async uploadFile(
    file: Buffer | NodeJS.ReadableStream,
    originalName: string,
    tenantId?: number,
    userId?: number,
  ): Promise<{ url: string; fileName: string; ossPath: string }> {
    if (!this.enabled || !this.client) {
      throw new BadRequestException('COS 服务未启用');
    }

    // 生成唯一文件名，图片和文件分开存储
    const fileExt = path.extname(originalName).toLowerCase();
    const uniqueId = randomBytes(16).toString('hex');

    // 判断是否为图片
    const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'];
    const isImage = imageExts.includes(fileExt);

    // 图片: img/{id}.ext, 文件: file/{id}.ext
    const cosPath = isImage ? `img/${uniqueId}${fileExt}` : `file/${uniqueId}${fileExt}`;

    try {
      // 上传到 COS
      await new Promise<COS.PutObjectResult>((resolve, reject) => {
        this.client!.putObject(
          {
            Bucket: this.bucket,
            Region: this.region,
            Key: cosPath,
            Body: file as Buffer,
          },
          (err, data) => {
            if (err) {
              reject(err);
            } else {
              resolve(data);
            }
          },
        );
      });

      // 构建访问URL
      const url = `https://${this.bucket}.cos.${this.region}.myqcloud.com/${cosPath}`;

      // 返回文件信息
      return {
        url,
        fileName: originalName,
        ossPath: cosPath,
      };
    } catch (error: any) {
      console.error('COS 上传失败:', error);
      throw new BadRequestException(`文件上传失败: ${error.message}`);
    }
  }

  /**
   * 删除 COS 文件
   * @param ossPath COS 文件路径
   */
  async deleteFile(ossPath: string): Promise<void> {
    if (!this.enabled || !this.client) {
      throw new BadRequestException('COS 服务未启用');
    }

    try {
      await new Promise<void>((resolve, reject) => {
        this.client!.deleteObject(
          {
            Bucket: this.bucket,
            Region: this.region,
            Key: ossPath,
          },
          (err) => {
            if (err) {
              reject(err);
            } else {
              resolve();
            }
          },
        );
      });
    } catch (error: any) {
      console.error('COS 删除失败:', error);
      throw new BadRequestException(`文件删除失败: ${error.message}`);
    }
  }

  /**
   * 获取文件的签名 URL（用于私有 Bucket）
   * @param ossPath COS 文件路径
   * @param expires 过期时间（秒），默认 3600
   */
  async getSignedUrl(ossPath: string, expires: number = 3600): Promise<string> {
    if (!this.enabled || !this.client) {
      throw new BadRequestException('COS 服务未启用');
    }

    try {
      const url = this.client.getObjectUrl(
        {
          Bucket: this.bucket,
          Region: this.region,
          Key: ossPath,
          Sign: true,
          Expires: expires,
        },
      );
      return url;
    } catch (error: any) {
      console.error('获取签名 URL 失败:', error);
      throw new BadRequestException(`获取文件链接失败: ${error.message}`);
    }
  }

  /**
   * 检查文件是否存在
   * @param ossPath COS 文件路径
   */
  async exists(ossPath: string): Promise<boolean> {
    if (!this.enabled || !this.client) {
      return false;
    }

    try {
      await new Promise<void>((resolve, reject) => {
        this.client!.headObject(
          {
            Bucket: this.bucket,
            Region: this.region,
            Key: ossPath,
          },
          (err) => {
            if (err) {
              reject(err);
            } else {
              resolve();
            }
          },
        );
      });
      return true;
    } catch (error: any) {
      if (error.statusCode === 404) {
        return false;
      }
      throw error;
    }
  }

  /**
   * 从URL下载文件到Buffer
   * @param url 文件URL
   * @returns 文件Buffer
   */
  private async downloadFromUrl(url: string): Promise<Buffer> {
    this.logger.log(`开始下载文件: ${url.substring(0, 100)}...`);
    const response = await axios.get(url, {
      responseType: 'arraybuffer',
      timeout: 120000, // 2分钟超时
    });
    this.logger.log(`文件下载完成，大小: ${response.data.length} 字节`);
    return Buffer.from(response.data);
  }

  /**
   * 上传AI 3D模型文件到COS
   * 支持ZIP格式（会解压提取GLB）和直接的GLB格式
   * @param url 模型文件URL
   * @param taskId 任务ID（用于目录隔离）
   * @param index 文件索引（多个文件时区分）
   * @returns 上传后的COS URL
   */
  async uploadAI3DModel(
    url: string,
    taskId: number,
    index: number = 0,
  ): Promise<{ modelUrl: string; ossPath: string }> {
    if (!this.enabled || !this.client) {
      throw new BadRequestException('COS 服务未启用');
    }

    try {
      // 下载文件
      const fileBuffer = await this.downloadFromUrl(url);

      // 检查是否是ZIP文件
      const isZip = url.toLowerCase().includes('.zip') ||
        (fileBuffer[0] === 0x50 && fileBuffer[1] === 0x4b); // ZIP magic number

      let modelBuffer: Buffer;
      let modelExt = '.glb';

      if (isZip) {
        this.logger.log('检测到ZIP文件，开始解压...');
        // 解压ZIP文件
        const zip = new AdmZip(fileBuffer);
        const zipEntries = zip.getEntries();

        // 打印ZIP中的所有文件，便于调试
        const fileNames = zipEntries.map(e => e.entryName);
        this.logger.log(`ZIP包含文件: ${fileNames.join(', ')}`);

        // 按优先级查找3D模型文件
        const modelFormats = ['.glb', '.gltf', '.obj', '.fbx', '.stl', '.usdz', '.usdc'];
        let modelEntry: any = null;

        for (const format of modelFormats) {
          modelEntry = zipEntries.find(entry =>
            entry.entryName.toLowerCase().endsWith(format)
          );
          if (modelEntry) {
            modelExt = format;
            break;
          }
        }

        if (!modelEntry) {
          throw new Error(`ZIP文件中未找到3D模型文件，包含: ${fileNames.join(', ')}`);
        }

        this.logger.log(`在ZIP中找到模型文件: ${modelEntry.entryName}`);
        modelBuffer = modelEntry.getData();
      } else {
        // 直接是GLB/GLTF文件
        modelBuffer = fileBuffer;
        if (url.toLowerCase().includes('.gltf')) {
          modelExt = '.gltf';
        }
      }

      // 生成COS路径（简化：3d/{taskId}.glb 或 3d/{taskId}_1.glb）
      const cosPath = index === 0 ? `3d/${taskId}${modelExt}` : `3d/${taskId}_${index}${modelExt}`;

      // 上传到COS
      await new Promise<COS.PutObjectResult>((resolve, reject) => {
        this.client!.putObject(
          {
            Bucket: this.bucket,
            Region: this.region,
            Key: cosPath,
            Body: modelBuffer,
          },
          (err, data) => {
            if (err) {
              reject(err);
            } else {
              resolve(data);
            }
          },
        );
      });

      const modelUrl = `https://${this.bucket}.cos.${this.region}.myqcloud.com/${cosPath}`;
      this.logger.log(`模型文件上传成功: ${modelUrl}`);

      return { modelUrl, ossPath: cosPath };
    } catch (error: any) {
      this.logger.error(`上传AI 3D模型失败: ${error.message}`, error.stack);
      throw new BadRequestException(`模型文件上传失败: ${error.message}`);
    }
  }

  /**
   * 上传AI 3D预览图到COS
   * @param url 预览图URL
   * @param taskId 任务ID（用于目录隔离）
   * @param index 文件索引（多个文件时区分）
   * @returns 上传后的COS URL
   */
  async uploadAI3DPreview(
    url: string,
    taskId: number,
    index: number = 0,
  ): Promise<{ previewUrl: string; ossPath: string }> {
    if (!this.enabled || !this.client) {
      throw new BadRequestException('COS 服务未启用');
    }

    try {
      // 下载文件
      const fileBuffer = await this.downloadFromUrl(url);

      // 从URL中提取扩展名
      let ext = '.png';
      const urlPath = new URL(url).pathname;
      const urlExt = path.extname(urlPath).toLowerCase();
      if (['.jpg', '.jpeg', '.png', '.webp'].includes(urlExt)) {
        ext = urlExt;
      }

      // 生成COS路径（简化：3d/{taskId}_p.png 或 3d/{taskId}_p1.png）
      const cosPath = index === 0 ? `3d/${taskId}_p${ext}` : `3d/${taskId}_p${index}${ext}`;

      // 上传到COS
      await new Promise<COS.PutObjectResult>((resolve, reject) => {
        this.client!.putObject(
          {
            Bucket: this.bucket,
            Region: this.region,
            Key: cosPath,
            Body: fileBuffer,
          },
          (err, data) => {
            if (err) {
              reject(err);
            } else {
              resolve(data);
            }
          },
        );
      });

      const previewUrl = `https://${this.bucket}.cos.${this.region}.myqcloud.com/${cosPath}`;
      this.logger.log(`预览图上传成功: ${previewUrl}`);

      return { previewUrl, ossPath: cosPath };
    } catch (error: any) {
      this.logger.error(`上传AI 3D预览图失败: ${error.message}`, error.stack);
      throw new BadRequestException(`预览图上传失败: ${error.message}`);
    }
  }
}
