import { Injectable, Inject, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cron, CronExpression } from '@nestjs/schedule';
import * as fs from 'fs';
import { SnapshotRepository } from '@/infrastructure/persistence/repositories/snapshot.repository';
import { MinioStorageAdapter } from '@/infrastructure/storage/minio-storage.adapter';
import { LocalStorageAdapter } from '@/infrastructure/storage/local-storage.adapter';
import { SnapshotGateway } from '@/api/gateways/snapshot.gateway';
import {
  BackupHandler,
  BACKUP_HANDLER_TOKEN,
} from '@/domain/interfaces/backup-handler.interface';
import {
  BackupTarget,
  BACKUP_TARGET_ORDER,
  SnapshotStatus,
  StorageType,
} from '@/domain/enums';

@Injectable()
export class SnapshotOrchestratorService implements OnModuleInit {
  private readonly logger = new Logger(SnapshotOrchestratorService.name);
  private readonly retentionHours: number;
  private runningTaskId: string | null = null;

  constructor(
    private readonly repo: SnapshotRepository,
    private readonly minioStorage: MinioStorageAdapter,
    private readonly localStorage: LocalStorageAdapter,
    private readonly gateway: SnapshotGateway,
    private readonly configService: ConfigService,
    @Inject(BACKUP_HANDLER_TOKEN)
    private readonly handlers: Map<BackupTarget, BackupHandler>,
  ) {
    this.retentionHours = this.configService.get<number>('SNAPSHOT_RETENTION_HOURS', 72);
  }

  async onModuleInit(): Promise<void> {
    // 1. 标记数据库中遗留的 RUNNING 任务为 FAILED
    const stale = await this.repo.findByStatus(SnapshotStatus.RUNNING);
    for (const task of stale) {
      await this.repo.updateTaskStatus(task.id, SnapshotStatus.FAILED, '服务重启，任务中断');
      this.localStorage.deleteTask(task.id);
      this.logger.warn(`遗留任务已标记失败: ${task.id}`);
    }

    // 2. 扫描临时目录，清理数据库中不存在的孤儿目录
    const dirIds = this.localStorage.listTaskDirs();
    for (const dirId of dirIds) {
      const task = await this.repo.findById(dirId);
      if (!task) {
        this.localStorage.deleteTask(dirId);
        this.logger.warn(`清理孤儿临时目录: ${dirId}`);
      }
    }
  }

  getAvailableTargets(): BackupTarget[] {
    return BACKUP_TARGET_ORDER.filter((t) => this.handlers.has(t));
  }

  isRunning(): boolean {
    return this.runningTaskId !== null;
  }

  async startSnapshot(data: {
    storageType: StorageType;
    storagePath: string;
    targets: BackupTarget[];
  }): Promise<string> {
    if (this.runningTaskId) {
      throw new Error('已有备份任务正在执行，请等待完成');
    }

    // 校验 targets 是否都可用
    const unavailable = data.targets.filter((t) => !this.handlers.has(t));
    if (unavailable.length > 0) {
      throw new Error(`以下备份目标不可用: ${unavailable.join(', ')}`);
    }

    // 按预定义顺序排序
    const sortedTargets = BACKUP_TARGET_ORDER.filter((t) => data.targets.includes(t));

    const task = await this.repo.createTask({
      storageType: data.storageType,
      storagePath: data.storagePath,
      targets: sortedTargets,
    });

    this.logger.log(`备份任务已创建: ${task.id}, targets: ${sortedTargets.join(',')}`);

    // 异步执行，不阻塞 HTTP 请求
    this.executeSnapshot(task.id).catch((err) => {
      this.logger.error(`备份任务异常: ${task.id}, ${err.message}`);
    });

    return task.id;
  }

  private async executeSnapshot(taskId: string): Promise<void> {
    this.runningTaskId = taskId;
    const startTime = Date.now();

    try {
      await this.repo.updateTaskStatus(taskId, SnapshotStatus.RUNNING);

      const task = await this.repo.findById(taskId);
      if (!task) throw new Error(`任务不存在: ${taskId}`);

      const targets: BackupTarget[] = JSON.parse(task.targets);
      const outputDir = this.localStorage.getTaskDir(taskId);

      this.gateway.emitTaskStarted(taskId, targets);

      // 串行执行各目标
      for (const target of targets) {
        await this.repo.updateDetailStatus(taskId, target, SnapshotStatus.RUNNING);
        this.gateway.emitProgress(taskId, target, 0, '开始备份...');

        try {
          const handler = this.handlers.get(target);
          if (!handler) {
            throw new Error(`备份处理器不存在: ${target}`);
          }

          let lastDbWriteTime = 0;
          const result = await handler.execute(outputDir, (percent, msg) => {
            this.gateway.emitProgress(taskId, target, percent, msg);
            // 每 2 秒写一次 DB，保证前端轮询能看到进度变化
            const now = Date.now();
            if (now - lastDbWriteTime >= 2000) {
              lastDbWriteTime = now;
              this.repo.updateDetailProgress(taskId, target, percent, msg).catch(() => {});
            }
          });

          // 如果存储到 MinIO，上传后删除本地临时文件
          if (task.storageType === StorageType.MINIO) {
            this.gateway.emitProgress(taskId, target, 99, '上传到 MinIO...');
            await this.minioStorage.upload(
              result.filePath,
              `${task.storagePath}/${result.fileName}`,
            );
            fs.unlinkSync(result.filePath);
          }

          await this.repo.completeDetail(taskId, target, result);
          this.gateway.emitTargetComplete(taskId, target, result.fileSize);
          this.logger.log(`备份目标完成: ${target}, 大小: ${result.fileSize} bytes`);
        } catch (error) {
          const errMsg = error instanceof Error ? error.message : String(error);
          await this.repo.failDetail(taskId, target, errMsg);
          this.gateway.emitError(taskId, target, errMsg);
          this.logger.error(`备份目标失败: ${target}, 错误: ${errMsg}`);
          // 单个目标失败不中断整体任务
        }
      }

      await this.repo.completeTask(taskId);
      const duration = Date.now() - startTime;
      const completedTask = await this.repo.findById(taskId);
      this.gateway.emitComplete(
        taskId,
        Number(completedTask?.totalSize ?? 0),
        duration,
      );
      this.logger.log(`备份任务完成: ${taskId}, 耗时: ${duration}ms`);
    } catch (error) {
      const errMsg = error instanceof Error ? error.message : String(error);
      await this.repo.updateTaskStatus(taskId, SnapshotStatus.FAILED, errMsg);
      this.gateway.emitError(taskId, undefined, errMsg);
      this.logger.error(`备份任务失败: ${taskId}, ${errMsg}`);
    } finally {
      this.runningTaskId = null;

      // MinIO 模式下清理空的临时目录
      const task = await this.repo.findById(taskId);
      if (task?.storageType === StorageType.MINIO) {
        this.localStorage.deleteTask(taskId);
      }
    }
  }

  async deleteSnapshot(taskId: string): Promise<void> {
    const task = await this.repo.findById(taskId);
    if (!task) throw new Error(`任务不存在: ${taskId}`);

    if (task.status === SnapshotStatus.RUNNING) {
      throw new Error('备份任务正在执行中，无法删除，请等待完成后再操作');
    }

    if (task.storageType === StorageType.LOCAL) {
      this.localStorage.deleteTask(taskId);
    } else if (task.storageType === StorageType.MINIO) {
      await this.minioStorage.deleteByPrefix(`${task.storagePath}/`);
    }

    await this.repo.deleteTask(taskId);
    this.logger.log(`备份已删除: ${taskId}`);
  }

  @Cron(CronExpression.EVERY_HOUR)
  async cleanupExpiredSnapshots(): Promise<void> {
    const deletedIds = this.localStorage.cleanupExpired(this.retentionHours);
    if (deletedIds.length > 0) {
      this.logger.log(`已清理 ${deletedIds.length} 个过期本地备份`);
      for (const id of deletedIds) {
        try {
          await this.repo.deleteTask(id);
        } catch {
          // 任务记录可能已不存在
        }
      }
    }
  }
}
