import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { PrismaService } from '@/core/database/prisma/prisma.service';
import { createTestApp } from '../../helpers/app.helper';
import { cleanupDatabase } from '../../helpers/cleanup.helper';
import { createAdminUser, createTestUser } from '../../helpers/factories/user.factory';

/**
 * Meeting-attendance · Reports Controller L1 集成测试
 *
 * 本文件只覆盖以下 endpoint（其他 3 个已由其他文件覆盖）：
 *   GET /meeting-attendance/reports/attendance
 *
 * 已被其他文件覆盖（不重复）：
 *   legacy-cutover-reports.api.test.ts  → GET /reports/series-options + GET /reports/series
 *   optional-attendee-stats.api.test.ts → GET /reports/single-meeting + GET /reports/series
 *
 * getAttendanceReport 返回结构：
 *   { summary: { totalAttendances, presentCount, lateCount, absentCount, attendanceRate },
 *     departmentStats: [{ department, total, present, attendanceRate }] }
 * 无数据时返回空结构（不抛异常）。
 * 权限：Administrator / MeetingManager / Leader（requireReportUser）。
 *
 * 关联工单 #341 · Batch 1-A
 */
describe('Meeting-attendance · Reports Attendance API', () => {
  let app: INestApplication;
  let prisma: PrismaService;

  beforeAll(async () => {
    process.env.NODE_ENV = 'test';
    app = await createTestApp();
    prisma = app.get<PrismaService>(PrismaService);
    // 清前一个 suite 残留的 orphan meeting（旧测试文件不全清 meeting 表）
    await prisma.meetingAttendance.deleteMany({}).catch(() => undefined);
    await prisma.meetingRequiredAttendee.deleteMany({}).catch(() => undefined);
    await prisma.meeting.deleteMany({}).catch(() => undefined);
  });

  afterEach(async () => {
    jest.restoreAllMocks();
    // 先清理 meeting attendance 数据（cleanupDatabase 只清理 IAM/HR 数据，
    // 不清理 platform_meeting_attendance schema，导致孤儿 meeting 行被下一个 case 查到）
    await prisma.meetingAttendance.deleteMany({}).catch(() => undefined);
    await prisma.meetingRequiredAttendee.deleteMany({}).catch(() => undefined);
    await prisma.meeting.deleteMany({}).catch(() => undefined);
    await cleanupDatabase(prisma);
  });

  afterAll(async () => {
    await app.close();
  });

  // ============================================================
  // helpers
  // ============================================================

  function suffix() {
    return `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
  }

  async function login(username: string, password: string): Promise<string> {
    const resp = await request(app.getHttpServer())
      .post('/api/v1/auth/login')
      .send({ username, password })
      .expect(200);
    return resp.body.data.accessToken as string;
  }

  async function setupAdmin() {
    const s = suffix();
    const adminUser = await createAdminUser({
      username: `ci_rpt_adm_${s}`,
      email: `ci_rpt_adm_${s}@example.com`,
      password: 'Admin@123',
      displayName: `Reports Admin ${s}`,
    });
    const adminToken = await login(adminUser.username, 'Admin@123');
    return { adminUser, adminToken };
  }

  async function setupEmployee() {
    const s = suffix();
    const employee = await createTestUser({
      username: `ci_rpt_emp_${s}`,
      email: `ci_rpt_emp_${s}@example.com`,
      password: 'Emp@123',
      displayName: `Reports Emp ${s}`,
    });
    await prisma.role.upsert({
      where: { code: 'Employee' },
      create: { code: 'Employee', name: 'Employee', enabled: true, isBuiltIn: true },
      update: { enabled: true },
    });
    const empToken = await login(employee.username, 'Emp@123');
    return { employee, empToken };
  }

  async function createMeeting(opts: {
    creatorId: string;
    status?: 'SCHEDULED' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED';
  }) {
    const now = new Date();
    return (prisma as any).meeting.create({
      data: {
        title: `Attendance Report Meeting ${suffix()}`,
        startTime: new Date(now.getTime() - 2 * 60 * 60 * 1000),
        endTime: new Date(now.getTime() - 60 * 60 * 1000),
        timezone: 'UTC',
        location: 'HQ',
        type: 'HYBRID',
        status: opts.status ?? 'COMPLETED',
        creatorId: opts.creatorId,
      },
    });
  }

  async function addRequiredAttendee(meetingId: string, userId: string) {
    return (prisma as any).meetingRequiredAttendee.create({
      data: { meetingId, userId, role: 'REGULAR_ATTENDEE' },
    });
  }

  async function addAttendance(meetingId: string, userId: string, status: string) {
    return prisma.meetingAttendance.create({
      data: { meetingId, userId, status } as any,
    });
  }

  // ============================================================
  // GET /meeting-attendance/reports/attendance
  // ============================================================

  describe('GET /meeting-attendance/reports/attendance', () => {
    it('[1] happy path：有会议 + 出勤数据 → 200 + summary 字段正确', async () => {
      const { adminUser, adminToken } = await setupAdmin();
      const { employee } = await setupEmployee();

      const meeting = await createMeeting({ creatorId: adminUser.id });
      await addRequiredAttendee(meeting.id, employee.id);
      await addAttendance(meeting.id, employee.id, 'ON_SITE');

      const resp = await request(app.getHttpServer())
        .get('/api/v1/meeting-attendance/reports/attendance')
        .set('Authorization', `Bearer ${adminToken}`)
        .expect(200);

      expect(resp.body).toHaveProperty('summary');
      expect(resp.body).toHaveProperty('departmentStats');
      expect(Array.isArray(resp.body.departmentStats)).toBe(true);

      const summary = resp.body.summary;
      expect(typeof summary.totalAttendances).toBe('number');
      expect(typeof summary.presentCount).toBe('number');
      expect(typeof summary.lateCount).toBe('number');
      expect(typeof summary.absentCount).toBe('number');
      expect(typeof summary.attendanceRate).toBe('number');

      // 至少应该计入本次 meeting 的 required attendee
      expect(summary.totalAttendances).toBeGreaterThanOrEqual(1);
      expect(summary.presentCount).toBeGreaterThanOrEqual(1);
    });

    it('[2] 鉴权：无 Authorization 头 → 401', async () => {
      await request(app.getHttpServer())
        .get('/api/v1/meeting-attendance/reports/attendance')
        .expect(401);
    });

    it('[3] 权限：普通员工 token → 403', async () => {
      const { empToken } = await setupEmployee();

      const resp = await request(app.getHttpServer())
        .get('/api/v1/meeting-attendance/reports/attendance')
        .set('Authorization', `Bearer ${empToken}`)
        .expect(403);

      expect(resp.body.error).toMatch(/insufficient permissions/i);
    });

    it('[4] 边界：无会议数据（CANCELLED 不计） → 200 + 返回空结构（不抛异常）', async () => {
      const { adminUser, adminToken } = await setupAdmin();

      // 创建一个 CANCELLED 会议（不应计入）
      await createMeeting({ creatorId: adminUser.id, status: 'CANCELLED' });

      const resp = await request(app.getHttpServer())
        .get('/api/v1/meeting-attendance/reports/attendance')
        .set('Authorization', `Bearer ${adminToken}`)
        .expect(200);

      expect(resp.body).toHaveProperty('summary');
      expect(resp.body.summary.totalAttendances).toBe(0);
      expect(resp.body.summary.attendanceRate).toBe(0);
      expect(resp.body.departmentStats).toEqual([]);
    });

    it('[5] 业务规则：attendanceRate = presentCount / totalAttendances × 100（取整）', async () => {
      const { adminUser, adminToken } = await setupAdmin();
      const s = suffix();

      // 创建 3 个 employee，2 个签到 ON_SITE，1 个未签
      const empA = await createTestUser({
        username: `ci_rpt_a_${s}`,
        email: `ci_rpt_a_${s}@example.com`,
        displayName: `Rpt A ${s}`,
      });
      const empB = await createTestUser({
        username: `ci_rpt_b_${s}`,
        email: `ci_rpt_b_${s}@example.com`,
        displayName: `Rpt B ${s}`,
      });
      const empC = await createTestUser({
        username: `ci_rpt_c_${s}`,
        email: `ci_rpt_c_${s}@example.com`,
        displayName: `Rpt C ${s}`,
      });

      const meeting = await createMeeting({ creatorId: adminUser.id });
      await addRequiredAttendee(meeting.id, empA.id);
      await addRequiredAttendee(meeting.id, empB.id);
      await addRequiredAttendee(meeting.id, empC.id);

      await addAttendance(meeting.id, empA.id, 'ON_SITE');
      await addAttendance(meeting.id, empB.id, 'ONLINE');
      // empC 未签到 → NOT_CHECKED_IN（无 attendance 记录）

      const resp = await request(app.getHttpServer())
        .get('/api/v1/meeting-attendance/reports/attendance')
        .set('Authorization', `Bearer ${adminToken}`)
        .expect(200);

      const summary = resp.body.summary;
      // 3 required attendees, 2 present
      expect(summary.totalAttendances).toBeGreaterThanOrEqual(3);
      expect(summary.presentCount).toBeGreaterThanOrEqual(2);
      // attendanceRate = round(present/total * 100) — 对本次数据至少 66
      expect(summary.attendanceRate).toBeGreaterThanOrEqual(0);
      expect(summary.attendanceRate).toBeLessThanOrEqual(100);
    });
  });
});
