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';

/**
 * 应到/实到口径仅算 REGULAR_ATTENDEE，OPTIONAL_ATTENDEE 单独计数。
 *
 * 场景（单会议）：
 *   - 2 个 regular：A 签到 ON_SITE / B 未签到
 *   - 1 个 optional：C 签到 ON_SITE
 *
 * 期望（GET /reports/single-meeting）：
 *   overallStats.totalRequired = 2（仅 regular）
 *   overallStats.totalAttended = 1
 *   overallStats.attendanceRate = 50
 *   overallStats.optionalTotal = 1
 *   overallStats.optionalAttended = 1
 *   statusDistribution.ON_SITE.count = 1（optional 不进状态分布）
 *
 * 场景（系列报表）扩展同口径，断言累计后的 optional 数。
 */

describe('Meeting Attendance — Optional Attendee Stats Separation', () => {
  let app: INestApplication;
  let prisma: PrismaService;

  beforeAll(async () => {
    process.env.NODE_ENV = 'test';
    app = await createTestApp();
    prisma = app.get<PrismaService>(PrismaService);
  });

  afterEach(async () => {
    jest.restoreAllMocks();
    await cleanupDatabase(prisma);
  });

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

  async function loginAsAdmin() {
    const suffix = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const adminUser = await createAdminUser({
      username: `opt_admin_${suffix}`,
      email: `opt_admin_${suffix}@example.com`,
      password: 'Admin@123',
      displayName: 'Optional Stats Admin',
    });
    const resp = await request(app.getHttpServer())
      .post('/api/v1/auth/login')
      .send({ username: adminUser.username, password: 'Admin@123' })
      .expect(200);
    return { adminUser, adminToken: resp.body.data.accessToken as string };
  }

  async function makeUser(tag: string) {
    const suffix = `${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
    return createTestUser({
      username: `opt_${tag}_${suffix}`,
      email: `opt_${tag}_${suffix}@example.com`,
      displayName: `Opt ${tag}`,
    } as any);
  }

  async function seedSingleMeeting(creatorId: string) {
    const userRegA = await makeUser('regA');
    const userRegB = await makeUser('regB');
    const userOptC = await makeUser('optC');

    const meeting = await prisma.meeting.create({
      data: {
        title: 'Opt Stats Meeting',
        startTime: new Date('2026-05-15T10:00:00Z'),
        endTime: new Date('2026-05-15T11:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        status: 'COMPLETED',
        creatorId,
      } as any,
    });

    await prisma.meetingRequiredAttendee.createMany({
      data: [
        { meetingId: meeting.id, userId: userRegA.id, role: 'REGULAR_ATTENDEE' },
        { meetingId: meeting.id, userId: userRegB.id, role: 'REGULAR_ATTENDEE' },
        { meetingId: meeting.id, userId: userOptC.id, role: 'OPTIONAL_ATTENDEE' },
      ] as any,
    });

    // A 签到 ON_SITE / B 未签到（NOT_CHECKED_IN，需要写入便于 statusDistribution 测试） /
    // C(optional) 签到 ON_SITE — 应到/实到口径里不应被算入
    await prisma.meetingAttendance.createMany({
      data: [
        {
          meetingId: meeting.id,
          userId: userRegA.id,
          status: 'ON_SITE',
          checkinTime: new Date('2026-05-15T10:05:00Z'),
        },
        {
          meetingId: meeting.id,
          userId: userRegB.id,
          status: 'NOT_CHECKED_IN',
        },
        {
          meetingId: meeting.id,
          userId: userOptC.id,
          status: 'ON_SITE',
          checkinTime: new Date('2026-05-15T10:08:00Z'),
        },
      ] as any,
    });

    return { meeting, userRegA, userRegB, userOptC };
  }

  test('GET /reports/single-meeting 应到/实到口径仅算 regular，optional 单独字段', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const { meeting } = await seedSingleMeeting(adminUser.id);

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

    expect(resp.body.overallStats).toMatchObject({
      totalRequired: 2,
      totalAttended: 1,
      attendanceRate: 50,
      optionalTotal: 1,
      optionalAttended: 1,
    });

    // statusDistribution 只统计 regular。regular A=ON_SITE / regular B=NOT_CHECKED_IN。
    // optional C 的 ON_SITE 不应进入 statusDistribution。
    const onSite = resp.body.statusDistribution.find((s: { status: string }) => s.status === 'ON_SITE');
    const notCheckedIn = resp.body.statusDistribution.find((s: { status: string }) => s.status === 'NOT_CHECKED_IN');
    expect(onSite.count).toBe(1);
    expect(notCheckedIn.count).toBe(1);
  });

  test('GET /reports/single-meeting optional 不进部门统计', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const { meeting } = await seedSingleMeeting(adminUser.id);

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

    // 部门聚合的 total 之和应等于 totalRequired（仅 regular，2 人）
    const deptTotal = resp.body.departmentStats.reduce(
      (sum: number, d: { total: number }) => sum + d.total,
      0,
    );
    expect(deptTotal).toBe(2);
  });

  test('GET /reports/single-meeting 纯 regular 会议返回 optionalTotal=0（防 undefined 短路前端 `> 0` 判定）', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const userReg = await makeUser('only_reg');

    const meeting = await prisma.meeting.create({
      data: {
        title: 'Pure Regular Meeting',
        startTime: new Date('2026-05-15T10:00:00Z'),
        endTime: new Date('2026-05-15T11:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        status: 'COMPLETED',
        creatorId: adminUser.id,
      } as any,
    });
    await prisma.meetingRequiredAttendee.create({
      data: { meetingId: meeting.id, userId: userReg.id, role: 'REGULAR_ATTENDEE' } as any,
    });
    await prisma.meetingAttendance.create({
      data: { meetingId: meeting.id, userId: userReg.id, status: 'NOT_CHECKED_IN' } as any,
    });

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

    expect(resp.body.overallStats.optionalTotal).toBe(0);
    expect(resp.body.overallStats.optionalAttended).toBe(0);
  });

  test('GET /reports/series 同口径：optional 单独累加', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();

    // 系列 + 一场会议
    const series = await prisma.meetingSeries.create({
      data: {
        title: 'Opt Stats Series',
        pattern: 'WEEKLY',
        frequency: 1,
        startDate: new Date('2026-05-01T00:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        creatorId: adminUser.id,
        isActive: true,
      } as any,
    });

    const userRegA = await makeUser('s_regA');
    const userOptB = await makeUser('s_optB');

    const meeting = await prisma.meeting.create({
      data: {
        title: 'Series Meeting',
        startTime: new Date('2026-05-08T10:00:00Z'),
        endTime: new Date('2026-05-08T11:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        status: 'COMPLETED',
        creatorId: adminUser.id,
        seriesId: series.id,
      } as any,
    });

    await prisma.meetingRequiredAttendee.createMany({
      data: [
        { meetingId: meeting.id, userId: userRegA.id, role: 'REGULAR_ATTENDEE' },
        { meetingId: meeting.id, userId: userOptB.id, role: 'OPTIONAL_ATTENDEE' },
      ] as any,
    });

    await prisma.meetingAttendance.createMany({
      data: [
        { meetingId: meeting.id, userId: userRegA.id, status: 'ON_SITE', checkinTime: new Date() },
        { meetingId: meeting.id, userId: userOptB.id, status: 'ON_SITE', checkinTime: new Date() },
      ] as any,
    });

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

    expect(resp.body.overallStats).toMatchObject({
      totalRequired: 1,
      totalAttended: 1,
      attendanceRate: 100,
      optionalTotal: 1,
      optionalAttended: 1,
    });
  });

  test('GET /reports/series lowAttendanceRanking 排除 OPTIONAL_ATTENDEE：optional 出勤率为 0 也不上低出勤率榜', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();

    const series = await prisma.meetingSeries.create({
      data: {
        title: 'Opt Ranking Series',
        pattern: 'WEEKLY',
        frequency: 1,
        startDate: new Date('2026-05-01T00:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        creatorId: adminUser.id,
        isActive: true,
      } as any,
    });

    const meeting = await prisma.meeting.create({
      data: {
        title: 'Opt Ranking Meeting',
        startTime: new Date('2026-05-08T10:00:00Z'),
        endTime: new Date('2026-05-08T11:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        status: 'COMPLETED',
        creatorId: adminUser.id,
        seriesId: series.id,
      } as any,
    });

    // 5 regular 全部出勤（100%）+ 1 optional 完全缺勤（0%）
    // 不过滤场景：personalStats 排序 → [opt(0%), reg×5(100%)]，threshold = 100%，
    //              filter < 100% 命中 opt → ranking = [opt]（误伤）
    // 过滤场景：rankable = [reg×5(100%)]，threshold = 100%，filter < 100% 无命中 → ranking = []
    const regs = await Promise.all([1, 2, 3, 4, 5].map((i) => makeUser(`rank_reg${i}`)));
    const opt = await makeUser('rank_opt');

    await prisma.meetingRequiredAttendee.createMany({
      data: [
        ...regs.map((u) => ({ meetingId: meeting.id, userId: u.id, role: 'REGULAR_ATTENDEE' })),
        { meetingId: meeting.id, userId: opt.id, role: 'OPTIONAL_ATTENDEE' },
      ] as any,
    });
    await prisma.meetingAttendance.createMany({
      data: [
        ...regs.map((u) => ({
          meetingId: meeting.id,
          userId: u.id,
          status: 'ON_SITE',
          checkinTime: new Date(),
        })),
        { meetingId: meeting.id, userId: opt.id, status: 'NOT_CHECKED_IN' },
      ] as any,
    });

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

    const ranking = resp.body.lowAttendanceRanking as Array<{ user: { id: string }; role: string }>;
    expect(ranking).toBeDefined();
    expect(ranking.find((r) => r.user.id === opt.id)).toBeUndefined();
    expect(ranking.every((r) => r.role !== 'OPTIONAL_ATTENDEE')).toBe(true);
  });
});
