import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { Server, Uid, Upload } from '@nice/tus';
import { FileStore } from '@nice/tus';
import { Request, Response } from 'express';
import { db, ResourceStatus } from '@nice/common';
import { getFilenameWithoutExt } from '@server/utils/file';
import { ResourceService } from '@server/models/resource/resource.service';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InjectQueue } from '@nestjs/bullmq';
import { Queue } from 'bullmq';
import { QueueJobType } from '@server/queue/types';
import { nanoid } from 'nanoid-cjs';
import { slugify } from 'transliteration';
const FILE_UPLOAD_CONFIG = {
  directory: process.env.UPLOAD_DIR,
  maxSizeBytes: 20_000_000_000, // 20GB
  expirationPeriod: 24 * 60 * 60 * 1000, // 24 hours
};
@Injectable()
export class TusService implements OnModuleInit {
  private readonly logger = new Logger(TusService.name);
  private tusServer: Server;
  constructor(
    private readonly resourceService: ResourceService,
    @InjectQueue('file-queue') private fileQueue: Queue,
  ) {}
  onModuleInit() {
    this.initializeTusServer();
    this.setupTusEventHandlers();
  }
  private initializeTusServer() {
    this.tusServer = new Server({
      namingFunction(req, metadata) {
        const safeFilename = slugify(metadata.filename);
        const now = new Date();
        const year = now.getFullYear();
        const month = String(now.getMonth() + 1).padStart(2, '0');
        const day = String(now.getDate()).padStart(2, '0');
        const uniqueId = nanoid(10);
        return `${year}/${month}/${day}/${uniqueId}/${safeFilename}`;
      },
      path: '/upload',
      datastore: new FileStore({
        directory: FILE_UPLOAD_CONFIG.directory,
        expirationPeriodInMilliseconds: FILE_UPLOAD_CONFIG.expirationPeriod,
      }),
      maxSize: FILE_UPLOAD_CONFIG.maxSizeBytes,
      postReceiveInterval: 1000,
      getFileIdFromRequest: (req, lastPath) => {
        const match = req.url.match(/\/upload\/(.+)/);
        return match ? match[1] : lastPath;
      },
    });
  }

  private setupTusEventHandlers() {
    this.tusServer.on('POST_CREATE', this.handleUploadCreate.bind(this));
    this.tusServer.on('POST_FINISH', this.handleUploadFinish.bind(this));
  }
  private getFileId(uploadId: string) {
    return uploadId.replace(/\/[^/]+$/, '');
  }
  private async handleUploadCreate(
    req: Request,
    res: Response,
    upload: Upload,
    url: string,
  ) {
    try {
      const fileId = this.getFileId(upload.id);
      //   const filename = upload.metadata.filename;
      await this.resourceService.create({
        data: {
          title: getFilenameWithoutExt(upload.metadata.filename),
          fileId, // 移除最后的文件名
          url: upload.id,
          meta: upload.metadata,
          status: ResourceStatus.UPLOADING,
        },
      });
    } catch (error) {
      this.logger.error('Failed to create resource during upload', error);
    }
  }

  private async handleUploadFinish(
    req: Request,
    res: Response,
    upload: Upload,
  ) {
    try {
      // console.log('upload.id', upload.id);
      // console.log('fileId', this.getFileId(upload.id));
      const resource = await this.resourceService.update({
        where: { fileId: this.getFileId(upload.id) },
        data: { status: ResourceStatus.UPLOADED },
      });
      this.fileQueue.add(
        QueueJobType.FILE_PROCESS,
        { resource },
        { jobId: resource.id },
      );
      this.logger.log(`Upload finished ${resource.url}`);
    } catch (error) {
      this.logger.error('Failed to update resource after upload', error);
    }
  }

  @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
  async cleanupExpiredUploads() {
    try {
      // Delete incomplete uploads older than 24 hours
      const deletedResources = await db.resource.deleteMany({
        where: {
          createdAt: {
            lt: new Date(Date.now() - FILE_UPLOAD_CONFIG.expirationPeriod),
          },
          status: ResourceStatus.UPLOADING,
        },
      });
      const expiredUploadCount = await this.tusServer.cleanUpExpiredUploads();
      this.logger.log(
        `Cleanup complete: ${deletedResources.count} resources and ${expiredUploadCount} uploads removed`,
      );
    } catch (error) {
      this.logger.error('Expired uploads cleanup failed', error);
    }
  }

  async handleTus(req: Request, res: Response) {
    // console.log(req)
    return this.tusServer.handle(req, res);
  }
}
