import { BadRequestException } from '@nestjs/common';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { ArchiveService } from './archive.service';

describe('ArchiveService purge sub-records', () => {
  const createService = (overrides?: {
    enrollment?: Record<string, unknown>;
    examScore?: Record<string, unknown>;
    learningRecord?: Record<string, unknown>;
    attachment?: Record<string, unknown>;
    scoreCount?: number;
  }) => {
    const enrollmentRepo = {
      findOne: jest.fn().mockResolvedValue({
        id: 1,
        status: 'archived',
        ...overrides?.enrollment,
      }),
      delete: jest.fn().mockResolvedValue({ affected: 1 }),
    };
    const examScoreRepo = {
      findOne: jest.fn().mockResolvedValue({
        id: 2,
        status: 'archived',
        ...overrides?.examScore,
      }),
      count: jest.fn().mockResolvedValue(overrides?.scoreCount ?? 0),
      delete: jest.fn().mockResolvedValue({ affected: 1 }),
    };
    const learningRecordRepo = {
      findOne: jest.fn().mockResolvedValue({
        id: 3,
        status: 'archived',
        ...overrides?.learningRecord,
      }),
      delete: jest.fn().mockResolvedValue({ affected: 1 }),
    };
    const attachmentRepo = {
      findOne: jest.fn().mockResolvedValue({
        id: 4,
        status: 'archived',
        filePath: 'x.pdf',
        ...overrides?.attachment,
      }),
      delete: jest.fn().mockResolvedValue({ affected: 1 }),
    };
    const service = new ArchiveService(
      {} as never,
      {} as never,
      enrollmentRepo as never,
      examScoreRepo as never,
      learningRecordRepo as never,
      {} as never,
      attachmentRepo as never,
      {} as never,
      {} as never,
    );
    return { service, enrollmentRepo, examScoreRepo, learningRecordRepo, attachmentRepo };
  };

  it('rejects non-archived sub-records', async () => {
    const { service, enrollmentRepo } = createService({ enrollment: { status: 'active' } });
    await expect(service.purgeEnrollment(1)).rejects.toThrow(
      new BadRequestException('仅已归档报名记录可以永久删除，请先归档'),
    );
    expect(enrollmentRepo.delete).not.toHaveBeenCalled();
  });

  it('rejects enrollments referenced by exam scores', async () => {
    const { service, enrollmentRepo } = createService({ scoreCount: 1 });
    await expect(service.purgeEnrollment(1)).rejects.toThrow(
      new BadRequestException('该报名记录已被考试成绩引用，无法永久删除'),
    );
    expect(enrollmentRepo.delete).not.toHaveBeenCalled();
  });

  it('deletes archived enrollment, exam score, and learning record', async () => {
    const { service, enrollmentRepo, examScoreRepo, learningRecordRepo } = createService();
    await expect(service.purgeEnrollment(1)).resolves.toEqual({
      message: '已永久删除报名记录（不可恢复）',
    });
    await expect(service.purgeExamScore(2)).resolves.toEqual({
      message: '已永久删除考试成绩（不可恢复）',
    });
    await expect(service.purgeLearningRecord(3)).resolves.toEqual({
      message: '已永久删除学习记录（不可恢复）',
    });
    expect(enrollmentRepo.delete).toHaveBeenCalledWith(1);
    expect(examScoreRepo.delete).toHaveBeenCalledWith(2);
    expect(learningRecordRepo.delete).toHaveBeenCalledWith(3);
  });

  it('deletes the attachment row and removes the disk file', async () => {
    const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'archive-purge-'));
    process.env.UPLOAD_DIR = tmpDir;
    const filePath = 'x.pdf';
    const fullPath = path.join(tmpDir, 'archive', filePath);
    fs.mkdirSync(path.dirname(fullPath), { recursive: true });
    fs.writeFileSync(fullPath, 'data');
    try {
      const { service, attachmentRepo } = createService({ attachment: { filePath } });
      await expect(service.purgeAttachment(4)).resolves.toEqual({
        message: '已永久删除附件（不可恢复）',
      });
      expect(fs.existsSync(fullPath)).toBe(false);
      expect(attachmentRepo.delete).toHaveBeenCalledWith(4);
    } finally {
      fs.rmSync(tmpDir, { recursive: true, force: true });
      delete process.env.UPLOAD_DIR;
    }
  });
});
