import {
  Injectable,
  Logger,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { v4 as uuidv4 } from 'uuid';
import * as mimeTypes from 'mime-types';
import { FileEntity, FileType, FileStatus } from '../domain/entities/file.entity';
import { MinioService } from '../minio/minio.service';
import {
  FileResponseDto,
  PresignedUrlDto,
  PresignedUrlResponseDto,
} from './dto/upload-file.dto';

// 允许的文件类型
const ALLOWED_IMAGE_TYPES = [
  'image/jpeg',
  'image/png',
  'image/gif',
  'image/webp',
  'image/svg+xml',
];

const ALLOWED_DOCUMENT_TYPES = [
  'application/pdf',
  'application/msword',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  'application/vnd.ms-excel',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  'text/plain',
  'text/csv',
  'text/markdown',
];

const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB

@Injectable()
export class FileService {
  private readonly logger = new Logger(FileService.name);

  constructor(
    @InjectRepository(FileEntity)
    private readonly fileRepository: Repository<FileEntity>,
    private readonly minioService: MinioService,
  ) {}

  /**
   * 获取预签名上传 URL
   */
  async getPresignedUploadUrl(
    userId: string,
    dto: PresignedUrlDto,
  ): Promise<PresignedUrlResponseDto> {
    const { fileName, mimeType, conversationId } = dto;

    // 验证文件类型
    const fileType = this.getFileType(mimeType);
    if (fileType === FileType.OTHER) {
      throw new BadRequestException(
        'Unsupported file type. Only images and documents are allowed.',
      );
    }

    // 生成唯一文件 ID 和存储路径
    const fileId = uuidv4();
    const extension = mimeTypes.extension(mimeType) || 'bin';
    const date = new Date();
    const datePath = `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')}`;
    const objectName = `uploads/${fileType}s/${datePath}/${userId}/${fileId}.${extension}`;

    // 创建文件记录 (状态为 uploading)
    const file = this.fileRepository.create({
      id: fileId,
      userId,
      conversationId: conversationId || null,
      originalName: fileName,
      storagePath: objectName,
      mimeType,
      type: fileType,
      size: 0,
      status: FileStatus.UPLOADING,
    });

    await this.fileRepository.save(file);

    // 获取预签名 URL (有效期 1 小时)
    const expiresIn = 3600;
    const uploadUrl = await this.minioService.getPresignedPutUrl(
      objectName,
      expiresIn,
    );

    return {
      uploadUrl,
      fileId,
      objectName,
      expiresIn,
    };
  }

  /**
   * 确认上传完成
   */
  async confirmUpload(
    userId: string,
    fileId: string,
    fileSize: number,
  ): Promise<FileResponseDto> {
    const file = await this.fileRepository.findOne({
      where: { id: fileId, userId },
    });

    if (!file) {
      throw new NotFoundException('File not found');
    }

    if (file.status !== FileStatus.UPLOADING) {
      throw new BadRequestException('File upload already confirmed');
    }

    if (fileSize > MAX_FILE_SIZE) {
      file.status = FileStatus.FAILED;
      file.errorMessage = 'File size exceeds maximum limit';
      await this.fileRepository.save(file);
      throw new BadRequestException('File size exceeds 50MB limit');
    }

    // 更新文件状态
    file.size = fileSize;
    file.status = FileStatus.READY;
    await this.fileRepository.save(file);

    // TODO: 触发后台处理 (生成缩略图、提取文本等)

    return this.toResponseDto(file);
  }

  /**
   * 直接上传文件 (适用于小文件)
   */
  async uploadFile(
    userId: string,
    file: Express.Multer.File,
    conversationId?: string,
  ): Promise<FileResponseDto> {
    const { originalname, mimetype, buffer, size } = file;

    // 验证文件大小
    if (size > MAX_FILE_SIZE) {
      throw new BadRequestException('File size exceeds 50MB limit');
    }

    // 验证文件类型
    const fileType = this.getFileType(mimetype);
    if (fileType === FileType.OTHER) {
      throw new BadRequestException(
        'Unsupported file type. Only images and documents are allowed.',
      );
    }

    // 生成存储路径
    const fileId = uuidv4();
    const extension = mimeTypes.extension(mimetype) || 'bin';
    const date = new Date();
    const datePath = `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')}`;
    const objectName = `uploads/${fileType}s/${datePath}/${userId}/${fileId}.${extension}`;

    // 上传到 MinIO
    await this.minioService.uploadFile(objectName, buffer, mimetype, {
      'x-amz-meta-original-name': encodeURIComponent(originalname),
      'x-amz-meta-user-id': userId,
    });

    // 创建文件记录
    const fileEntity = this.fileRepository.create({
      id: fileId,
      userId,
      conversationId: conversationId || null,
      originalName: originalname,
      storagePath: objectName,
      mimeType: mimetype,
      type: fileType,
      size,
      status: FileStatus.READY,
    });

    await this.fileRepository.save(fileEntity);

    this.logger.log(`File uploaded: ${fileId} by user ${userId}`);

    return this.toResponseDto(fileEntity);
  }

  /**
   * 获取文件信息
   */
  async getFile(userId: string, fileId: string): Promise<FileResponseDto> {
    const file = await this.fileRepository.findOne({
      where: { id: fileId, userId, status: FileStatus.READY },
    });

    if (!file) {
      throw new NotFoundException('File not found');
    }

    return this.toResponseDto(file);
  }

  /**
   * 获取文件下载 URL
   */
  async getDownloadUrl(userId: string, fileId: string): Promise<string> {
    const file = await this.fileRepository.findOne({
      where: { id: fileId, userId, status: FileStatus.READY },
    });

    if (!file) {
      throw new NotFoundException('File not found');
    }

    return this.minioService.getPresignedUrl(file.storagePath, 3600);
  }

  /**
   * 获取用户的所有文件
   */
  async getUserFiles(
    userId: string,
    conversationId?: string,
  ): Promise<FileResponseDto[]> {
    const where: Record<string, unknown> = {
      userId,
      status: FileStatus.READY,
    };

    if (conversationId) {
      where.conversationId = conversationId;
    }

    const files = await this.fileRepository.find({
      where,
      order: { createdAt: 'DESC' },
    });

    return Promise.all(files.map((f) => this.toResponseDto(f)));
  }

  /**
   * 删除文件 (软删除)
   */
  async deleteFile(userId: string, fileId: string): Promise<void> {
    const file = await this.fileRepository.findOne({
      where: { id: fileId, userId },
    });

    if (!file) {
      throw new NotFoundException('File not found');
    }

    file.status = FileStatus.DELETED;
    file.deletedAt = new Date();
    await this.fileRepository.save(file);

    this.logger.log(`File deleted: ${fileId} by user ${userId}`);
  }

  /**
   * 确定文件类型
   */
  private getFileType(mimeType: string): FileType {
    if (ALLOWED_IMAGE_TYPES.includes(mimeType)) {
      return FileType.IMAGE;
    }
    if (ALLOWED_DOCUMENT_TYPES.includes(mimeType)) {
      return FileType.DOCUMENT;
    }
    if (mimeType.startsWith('audio/')) {
      return FileType.AUDIO;
    }
    if (mimeType.startsWith('video/')) {
      return FileType.VIDEO;
    }
    return FileType.OTHER;
  }

  /**
   * 转换为响应 DTO
   */
  private async toResponseDto(file: FileEntity): Promise<FileResponseDto> {
    const dto: FileResponseDto = {
      id: file.id,
      originalName: file.originalName,
      mimeType: file.mimeType,
      type: file.type,
      size: Number(file.size),
      status: file.status,
      createdAt: file.createdAt,
    };

    if (file.status === FileStatus.READY) {
      dto.downloadUrl = await this.minioService.getPresignedUrl(
        file.storagePath,
        3600,
      );

      if (file.thumbnailPath) {
        dto.thumbnailUrl = await this.minioService.getPresignedUrl(
          file.thumbnailPath,
          3600,
        );
      }
    }

    return dto;
  }
}
