import { GenericCrudService } from '@/server/utils/generic-crud.service';
import { DataSource } from 'typeorm';
import { File } from './file.entity';
import { MinioService } from './minio.service';
// import { AppError } from '@/server/utils/errorHandler';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '@/server/utils/logger';
import axios from 'axios';

export class FileService extends GenericCrudService<File> {
  private readonly minioService: MinioService;

  constructor(dataSource: DataSource) {
    super(dataSource, File);
    this.minioService = new MinioService();
  }

  /**
   * 创建文件记录并生成预签名上传URL
   */
  async createFile(data: Partial<File>) {
    try {
      // 生成唯一文件存储路径
      const fileKey = `${data.uploadUserId}/${uuidv4()}-${data.name}`;
      
      // 生成MinIO上传策略
      const uploadPolicy = await this.minioService.generateUploadPolicy(fileKey);
      
      // 准备文件记录数据
      const fileData = {
        ...data,
        path: fileKey,
        uploadTime: new Date(),
        createdAt: new Date(),
        updatedAt: new Date()
      };
      
      // 保存文件记录到数据库
      const savedFile = await this.create(fileData as File);
      
      // 返回文件记录和上传策略
      return {
        file: savedFile,
        uploadPolicy
      };
    } catch (error) {
      logger.error('Failed to create file:', error);
      throw new Error('文件创建失败');
    }
  }

  /**
   * 删除文件记录及对应的MinIO文件
   */
  async deleteFile(id: number) {
    try {
      // 获取文件记录
      const file = await this.getById(id);
      if (!file) {
        throw new Error('文件不存在');
      }
      
      // 验证文件是否存在于MinIO
      const fileExists = await this.minioService.objectExists(this.minioService.bucketName, file.path);
      if (!fileExists) {
        logger.error(`File not found in MinIO: ${this.minioService.bucketName}/${file.path}`);
        // 仍然继续删除数据库记录，但记录警告日志
      } else {
        // 从MinIO删除文件
        await this.minioService.deleteObject(this.minioService.bucketName, file.path);
      }
      
      // 从数据库删除记录
      await this.delete(id);
      
      return true;
    } catch (error) {
      logger.error('Failed to delete file:', error);
      throw new Error('文件删除失败');
    }
  }

  /**
   * 获取文件访问URL
   */
  async getFileUrl(id: number) {
    const file = await this.getById(id);
    if (!file) {
      throw new Error('文件不存在');
    }
    
    return this.minioService.getPresignedFileUrl(this.minioService.bucketName, file.path);
  }

  /**
   * 获取文件下载URL（带Content-Disposition头）
   */
  async getFileDownloadUrl(id: number) {
    const file = await this.getById(id);
    if (!file) {
      throw new Error('文件不存在');
    }
    
    const url = await this.minioService.getPresignedFileDownloadUrl(
      this.minioService.bucketName,
      file.path,
      file.name
    );
    
    return {
      url,
      filename: file.name
    };
  }

  /**
   * 从URL下载文件并保存到MinIO，同时创建数据库记录
   */
  async downloadAndSaveFromUrl(
    url: string,
    fileData: {
      uploadUserId: number;
      mimeType?: string;
      customFileName?: string;
      customPath?: string;
    },
    options: {
      timeout?: number;
      retries?: number;
    } = {}
  ): Promise<{ file: File; url: string }> {
    try {
      const { uploadUserId, mimeType, customFileName, customPath } = fileData;
      const { timeout = 30000, retries = 0 } = options;

      logger.info('Downloading file from URL:', { url, uploadUserId });

      // 下载文件
      const response = await axios.get(url, {
        responseType: 'arraybuffer',
        timeout,
        validateStatus: (status) => status === 200
      });

      if (!response.data) {
        throw new Error('下载的文件内容为空');
      }

      // 获取文件名和MIME类型
      const originalFileName = customFileName || this.extractFileNameFromUrl(url);
      const detectedMimeType = mimeType || response.headers['content-type'] || 'application/octet-stream';
      const fileSize = Buffer.byteLength(response.data);

      // 生成唯一文件存储路径
      const fileKey = customPath
        ? `${customPath}${uploadUserId}/${uuidv4()}-${originalFileName}`
        : `${uploadUserId}/${uuidv4()}-${originalFileName}`;

      // 上传到MinIO
      await this.minioService.createObject(
        this.minioService.bucketName,
        fileKey,
        Buffer.from(response.data),
        detectedMimeType
      );

      // 创建文件记录
      const fileRecordData: Partial<File> = {
        name: originalFileName,
        path: fileKey,
        size: fileSize,
        type: detectedMimeType,
        uploadUserId,
        uploadTime: new Date(),
        createdAt: new Date(),
        updatedAt: new Date()
      };

      const savedFile = await this.create(fileRecordData as File);

      // 生成访问URL
      const fileUrl = this.minioService.getFileUrl(this.minioService.bucketName, fileKey);

      logger.info('File downloaded and saved successfully:', {
        fileId: savedFile.id,
        originalUrl: url,
        fileSize,
        mimeType: detectedMimeType
      });

      return {
        file: savedFile,
        url: fileUrl
      };
    } catch (error) {
      logger.error('Failed to download and save file from URL:', error);
      throw new Error(`从URL下载文件失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 从URL中提取文件名
   */
  private extractFileNameFromUrl(url: string): string {
    try {
      const urlObj = new URL(url);
      const pathname = urlObj.pathname;
      const fileName = pathname.substring(pathname.lastIndexOf('/') + 1);
      
      // 如果没有文件名或文件名无效，使用默认名称
      if (!fileName || fileName === '/' || fileName.includes('?')) {
        return `downloaded-file-${Date.now()}`;
      }
      
      // 移除查询参数
      return fileName.split('?')[0];
    } catch {
      return `downloaded-file-${Date.now()}`;
    }
  }

  /**
   * 创建多部分上传策略
   */
  async createMultipartUploadPolicy(data: Partial<File>, partCount: number) {
    try {
      // 生成唯一文件存储路径
      const fileKey = `${data.uploadUserId}/${uuidv4()}-${data.name}`;
      
      // 初始化多部分上传
      const uploadId = await this.minioService.createMultipartUpload(
        this.minioService.bucketName,
        fileKey
      );
      
      // 生成各部分上传URL
      const uploadUrls = await this.minioService.generateMultipartUploadUrls(
        this.minioService.bucketName,
        fileKey,
        uploadId,
        partCount
      );
      
      // 准备文件记录数据
      const fileData = {
        ...data,
        path: fileKey,
        uploadTime: new Date(),
        createdAt: new Date(),
        updatedAt: new Date()
      };
      
      // 保存文件记录到数据库
      const savedFile = await this.create(fileData as File);
      
      // 返回文件记录和上传策略
      return {
        file: savedFile,
        uploadId,
        uploadUrls,
        bucket: this.minioService.bucketName,
        key: fileKey
      };
    } catch (error) {
      logger.error('Failed to create multipart upload policy:', error);
      throw new Error('创建多部分上传策略失败');
    }
  }

  /**
   * 完成分片上传
   */
  async completeMultipartUpload(data: {
    uploadId: string;
    bucket: string;
    key: string;
    parts: Array<{ partNumber: number; etag: string }>;
  }) {
    try {
      logger.db('Starting multipart upload completion:', {
        uploadId: data.uploadId,
        bucket: data.bucket,
        key: data.key,
        partsCount: data.parts.length
      });

      // 完成MinIO分片上传 - 注意格式转换
      const result = await this.minioService.completeMultipartUpload(
        data.bucket,
        data.key,
        data.uploadId,
        data.parts.map(part => ({ PartNumber: part.partNumber, ETag: part.etag }))
      );
      
      // 查找文件记录并更新
      const file = await this.repository.findOneBy({ path: data.key });
      if (!file) {
        throw new Error('文件记录不存在');
      }
      
      // 更新文件大小等信息
      file.size = result.size;
      file.updatedAt = new Date();
      await this.repository.save(file);
      
      // 生成文件访问URL
      const url = this.minioService.getFileUrl(data.bucket, data.key);
      
      logger.db('Multipart upload completed successfully:', {
        fileId: file.id,
        size: result.size,
        key: data.key
      });
      
      return {
        fileId: file.id,
        url,
        key: data.key,
        size: result.size
      };
    } catch (error) {
      logger.error('Failed to complete multipart upload:', error);
      throw new Error('完成分片上传失败');
    }
  }
}