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

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) {
    // 获取文件记录
    const file = await this.getById(id);
    if (!file) {
      throw new Error('文件不存在');
    }

    try {
      // 验证文件是否存在于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
    };
  }

  /**
   * 创建多部分上传策略
   */
  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 }>;
  }) {
    logger.db('Starting multipart upload completion:', {
      uploadId: data.uploadId,
      bucket: data.bucket,
      key: data.key,
      partsCount: data.parts.length
    });

    // 查找文件记录
    const file = await this.repository.findOneBy({ path: data.key });
    if (!file) {
      throw new Error('文件记录不存在');
    }

    try {
      // 完成MinIO分片上传 - 注意格式转换
      const result = await this.minioService.completeMultipartUpload(
        data.bucket,
        data.key,
        data.uploadId,
        data.parts.map(part => ({ PartNumber: part.partNumber, ETag: part.etag }))
      );

      // 更新文件大小等信息
      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('完成分片上传失败');
    }
  }

  /**
   * 保存文件记录并将文件内容直接上传到MinIO
   * @param fileData - 文件基础信息
   * @param fileContent - 文件内容（Buffer）
   * @param contentType - 文件MIME类型
   * @returns 保存的文件记录和文件访问URL
   */
  async saveFile(
    fileData: {
      name: string;
      size: number;
      mimeType: string;
      uploadUserId: number;
      [key: string]: any;
    },
    fileContent: Buffer,
    contentType?: string
  ) {
    try {
      logger.db('Starting saveFile process:', {
        filename: fileData.name,
        size: fileData.size,
        mimeType: fileData.mimeType,
        uploadUserId: fileData.uploadUserId
      });

      // 生成唯一文件存储路径
      const fileKey = `${fileData.uploadUserId}/${uuidv4()}-${fileData.name}`;

      // 确保存储桶存在
      await this.minioService.ensureBucketExists();

      // 直接上传文件内容到MinIO
      const fileUrl = await this.minioService.createObject(
        this.minioService.bucketName,
        fileKey,
        fileContent,
        contentType || fileData.mimeType
      );

      // 准备文件记录数据
      const completeFileData = {
        ...fileData,
        path: fileKey,
        uploadTime: new Date(),
      };

      // 保存文件记录到数据库
      const savedFile = await this.create(completeFileData as any);

      logger.db('File saved successfully:', {
        fileId: savedFile.id,
        filename: savedFile.name,
        size: savedFile.size,
        url: fileUrl
      });

      return {
        file: savedFile,
        url: fileUrl
      };
    } catch (error) {
      logger.error('Failed to save file:', error);
      throw new Error(`文件保存失败: ${error instanceof Error ? error.message : '未知错误'}`);
    }
  }

  /**
   * 保存文件记录并将文件内容直接上传到MinIO（支持自定义存储路径）
   * @param fileData - 文件基础信息
   * @param fileContent - 文件内容（Buffer）
   * @param customPath - 自定义存储路径（可选）
   * @param contentType - 文件MIME类型
   * @returns 保存的文件记录和文件访问URL
   */
  async saveFileWithCustomPath(
    fileData: {
      name: string;
      size: number;
      mimeType: string;
      uploadUserId: number;
      [key: string]: any;
    },
    fileContent: Buffer,
    customPath?: string,
    contentType?: string
  ) {
    try {
      logger.db('Starting saveFileWithCustomPath process:', {
        filename: fileData.name,
        size: fileData.size,
        mimeType: fileData.mimeType,
        uploadUserId: fileData.uploadUserId,
        customPath: customPath || 'auto-generated'
      });

      // 使用自定义路径或生成唯一文件存储路径
      const fileKey = customPath || `${fileData.uploadUserId}/${uuidv4()}-${fileData.name}`;

      // 确保存储桶存在
      await this.minioService.ensureBucketExists();

      // 直接上传文件内容到MinIO
      const fileUrl = await this.minioService.createObject(
        this.minioService.bucketName,
        fileKey,
        fileContent,
        contentType || fileData.mimeType
      );

      // 准备文件记录数据
      const completeFileData = {
        ...fileData,
        path: fileKey,
        uploadTime: new Date(),
        // createdAt: new Date(),
        // updatedAt: new Date()
      };

      // 保存文件记录到数据库
      const savedFile = await this.create(completeFileData as any);

      logger.db('File saved with custom path successfully:', {
        fileId: savedFile.id,
        filename: savedFile.name,
        size: savedFile.size,
        path: fileKey,
        url: fileUrl
      });

      return {
        file: savedFile,
        url: fileUrl
      };
    } catch (error) {
      logger.error('Failed to save file with custom path:', error);
      throw new Error(`文件保存失败: ${error instanceof Error ? error.message : '未知错误'}`);
    }
  }

  /**
   * 从URL下载文件并保存到MinIO
   * @param url - 文件URL
   * @param fileData - 文件基础信息（不含name和size，将自动获取）
   * @param options - 可选配置
   * @returns 保存的文件记录和文件访问URL
   */
  async downloadAndSaveFromUrl(
    url: string,
    fileData: {
      uploadUserId: number;
      mimeType?: string;
      customFileName?: string;
      customPath?: string;
      [key: string]: any;
    },
    options?: {
      timeout?: number;
      retries?: number;
    }
  ) {
    try {
      const axios = require('axios');
      
      logger.db('Starting downloadAndSaveFromUrl process:', {
        url,
        uploadUserId: fileData.uploadUserId,
        customFileName: fileData.customFileName,
        customPath: fileData.customPath
      });

      // 下载文件
      const response = await axios.get(url, {
        responseType: 'arraybuffer',
        timeout: options?.timeout || 30000,
        maxRedirects: 5,
        headers: {
          'User-Agent': 'Mozilla/5.0 (compatible; FileDownloader/1.0)'
        }
      });

      const buffer = Buffer.from(response.data);
      
      // 从URL或响应头中获取文件名
      let fileName = fileData.customFileName;
      if (!fileName) {
        // 尝试从Content-Disposition头获取文件名
        const contentDisposition = response.headers['content-disposition'];
        if (contentDisposition) {
          const filenameMatch = contentDisposition.match(/filename[*]?=(?:utf-8'')?(.+)/i);
          if (filenameMatch) {
            fileName = decodeURIComponent(filenameMatch[1].replace(/['"]/g, ''));
          }
        }
        
        // 从URL路径获取文件名
        if (!fileName) {
          const urlPath = new URL(url).pathname;
          fileName = urlPath.split('/').pop() || `file_${Date.now()}`;
        }
      }

      // 确保文件有扩展名
      if (!fileName.includes('.') && fileData.mimeType) {
        const ext = this.getExtensionFromMimeType(fileData.mimeType);
        if (ext) {
          fileName += `.${ext}`;
        }
      }

      // 确定MIME类型
      let mimeType = fileData.mimeType || response.headers['content-type'];
      if (!mimeType || mimeType === 'application/octet-stream') {
        mimeType = this.inferMimeType(fileName);
      }

      // 保存文件
      const saveResult = await this.saveFileWithCustomPath(
        {
          ...fileData,
          name: fileName,
          size: buffer.length,
          mimeType,
          fileType: this.getFileTypeFromMimeType(mimeType)
        },
        buffer,
        fileData.customPath,
        mimeType
      );

      logger.db('Download and save completed successfully:', {
        fileId: saveResult.file.id,
        fileName,
        size: buffer.length,
        url: saveResult.url
      });

      return saveResult;
    } catch (error) {
      logger.error('Failed to download and save file from URL:', {
        url,
        error: error instanceof Error ? error.message : '未知错误',
        stack: error instanceof Error ? error.stack : undefined
      });
      throw new Error(`从URL下载文件失败: ${error instanceof Error ? error.message : '未知错误'}`);
    }
  }

  /**
   * 根据MIME类型获取文件扩展名
   */
  private getExtensionFromMimeType(mimeType: string): string | null {
    const mimeMap: Record<string, string> = {
      'image/jpeg': 'jpg',
      'image/png': 'png',
      'image/gif': 'gif',
      'image/webp': 'webp',
      'image/svg+xml': 'svg',
      'application/pdf': 'pdf',
      'text/plain': 'txt',
      'application/json': 'json',
      'application/xml': 'xml',
      'video/mp4': 'mp4',
      'audio/mp3': 'mp3'
    };
    return mimeMap[mimeType] || null;
  }

  /**
   * 根据文件名推断MIME类型
   */
  private inferMimeType(fileName: string): string {
    const ext = fileName.toLowerCase().split('.').pop();
    const extMap: Record<string, string> = {
      'jpg': 'image/jpeg',
      'jpeg': 'image/jpeg',
      'png': 'image/png',
      'gif': 'image/gif',
      'webp': 'image/webp',
      'svg': 'image/svg+xml',
      'pdf': 'application/pdf',
      'txt': 'text/plain',
      'json': 'application/json',
      'xml': 'application/xml',
      'mp4': 'video/mp4',
      'mp3': 'audio/mp3',
      'wav': 'audio/wav'
    };
    return extMap[ext || ''] || 'application/octet-stream';
  }

  /**
   * 根据MIME类型获取文件类型
   */
  private getFileTypeFromMimeType(mimeType: string): string {
    if (mimeType.startsWith('image/')) return 'image';
    if (mimeType.startsWith('video/')) return 'video';
    if (mimeType.startsWith('audio/')) return 'audio';
    if (mimeType === 'application/pdf') return 'document';
    if (mimeType.startsWith('text/')) return 'document';
    return 'other';
  }
}