import {
  Controller,
  Get,
  Post,
  Delete,
  Param,
  Body,
  Query,
  Res,
  Req,
  NotFoundException,
  BadRequestException,
  ConflictException,
  Logger,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger';
import { Request, Response } from 'express';
import * as fs from 'fs';
import * as path from 'path';
import { CreateSnapshotDto } from '../dto/create-snapshot.dto';
import { toSnapshotResponse } from '../dto/snapshot.response';
import { SnapshotOrchestratorService } from '@/application/services/snapshot-orchestrator.service';
import { SnapshotRepository } from '@/infrastructure/persistence/repositories/snapshot.repository';
import { StorageType } from '@/domain/enums';

@ApiTags('快照备份')
@Controller('snapshots')
export class SnapshotController {
  private readonly logger = new Logger(SnapshotController.name);

  constructor(
    private readonly orchestrator: SnapshotOrchestratorService,
    private readonly repo: SnapshotRepository,
  ) {}

  @Get('targets')
  @ApiOperation({ summary: '获取可用备份目标列表' })
  getAvailableTargets() {
    return {
      targets: this.orchestrator.getAvailableTargets(),
      isRunning: this.orchestrator.isRunning(),
    };
  }

  @Post()
  @ApiOperation({ summary: '创建备份任务' })
  @ApiResponse({ status: 201, description: '任务已创建，异步执行' })
  @ApiResponse({ status: 409, description: '已有任务在执行' })
  async createSnapshot(@Body() dto: CreateSnapshotDto) {
    if (this.orchestrator.isRunning()) {
      throw new ConflictException('已有备份任务正在执行，请等待完成');
    }

    const taskId = await this.orchestrator.startSnapshot(dto);
    return { taskId, message: '备份任务已启动' };
  }

  @Get()
  @ApiOperation({ summary: '备份历史列表' })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  async listSnapshots(
    @Query('page') page?: string,
    @Query('limit') limit?: string,
  ) {
    const p = Math.max(1, parseInt(page || '1', 10) || 1);
    const l = Math.min(100, Math.max(1, parseInt(limit || '20', 10) || 20));
    const result = await this.repo.findAll(p, l);
    return {
      tasks: result.tasks.map(toSnapshotResponse),
      total: result.total,
      page: result.page,
      limit: result.limit,
    };
  }

  @Get(':id')
  @ApiOperation({ summary: '备份任务详情' })
  async getSnapshot(@Param('id') id: string) {
    const task = await this.repo.findById(id);
    if (!task) throw new NotFoundException('备份任务不存在');
    return toSnapshotResponse(task);
  }

  @Delete(':id')
  @ApiOperation({ summary: '删除备份' })
  async deleteSnapshot(@Param('id') id: string) {
    const task = await this.repo.findById(id);
    if (!task) throw new NotFoundException('备份任务不存在');
    if (task.status === 'RUNNING') {
      throw new ConflictException('备份任务正在执行中，无法删除，请等待完成后再操作');
    }
    await this.orchestrator.deleteSnapshot(id);
    return { message: '备份已删除' };
  }

  @Get(':id/download/:target')
  @ApiOperation({ summary: '下载备份文件 (仅 LOCAL 模式)' })
  @ApiResponse({ status: 200, description: '文件内容' })
  @ApiResponse({ status: 206, description: '部分内容 (断点续传)' })
  async downloadFile(
    @Param('id') id: string,
    @Param('target') target: string,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const task = await this.repo.findById(id);
    if (!task) throw new NotFoundException('备份任务不存在');
    if (task.storageType !== StorageType.LOCAL) {
      throw new BadRequestException('仅本地存储模式支持下载');
    }

    const detail = task.details.find((d) => d.target === target);
    if (!detail || !detail.fileName) {
      throw new NotFoundException('备份文件不存在');
    }

    const tempDir = process.env.SNAPSHOT_TEMP_DIR || './data/snapshots';
    const filePath = path.join(tempDir, id, detail.fileName);

    if (!fs.existsSync(filePath)) {
      throw new NotFoundException('备份文件已过期或被删除');
    }

    const stat = fs.statSync(filePath);
    const fileSize = stat.size;
    const range = req.headers.range;

    res.setHeader('Accept-Ranges', 'bytes');
    res.setHeader('Content-Type', 'application/gzip');
    res.setHeader(
      'Content-Disposition',
      `attachment; filename="${encodeURIComponent(detail.fileName)}"`,
    );

    if (range) {
      const parts = range.replace(/bytes=/, '').split('-');
      const start = parseInt(parts[0], 10);
      const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;

      if (start >= fileSize || end >= fileSize || start > end) {
        res.status(416);
        res.setHeader('Content-Range', `bytes */${fileSize}`);
        res.end();
        return;
      }

      const chunkSize = end - start + 1;
      res.status(206);
      res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`);
      res.setHeader('Content-Length', chunkSize);
      fs.createReadStream(filePath, { start, end }).pipe(res);
    } else {
      res.setHeader('Content-Length', fileSize);
      fs.createReadStream(filePath).pipe(res);
    }
  }
}
