import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { PrismaService } from '@/core/database/prisma/prisma.service';
import { SERIES_IDS } from '@/modules/meeting-attendance/constants/legacy-series-mapping';
import { createTestApp } from '../../helpers/app.helper';
import { cleanupDatabase } from '../../helpers/cleanup.helper';
import { createAdminUser, createTestUser } from '../../helpers/factories/user.factory';

/**
 * 会议出勤迁移收尾 - 报表合并集成测试
 *
 * 覆盖：
 *   1. GET /reports/series-options 返回 hasLegacy 标记
 *   2. getSeriesReport includeLegacy=0 → 仅新系列
 *   3. getSeriesReport includeLegacy=1 无冲突日期 → 合并返回
 *   4. getSeriesReport includeLegacy=1 含 3/16 → 排除新系列 3/16,改以旧为准
 *   5. effectiveStartTime = 首场有数据 meeting 的 startTime
 *
 * 常量里 hardcode 了生产 seriesId(cuid),测试数据库用同样 id seed 使常量命中。
 * cleanupDatabase 按 creatorId 级联清理,无污染。
 */

const NEW_FFAI_SERIES_ID = SERIES_IDS.NEW_FFAI;
const OLD_FFAI_SERIES_ID = SERIES_IDS.OLD_FFAI;
const OVERRIDE_DATE = '2026-03-16';

describe('Legacy Cutover Reports Integration Tests', () => {
  let app: INestApplication;
  let prisma: PrismaService;

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

  async function purgeLegacyFixtures() {
    // 主动清 hardcode 的 series (级联 CASCADE 清 meetings / attendances / required_attendees),
    // 必须在 cleanupDatabase (按 creatorId 级联) 之前,因为 series.creator_id FK 是 ON DELETE RESTRICT
    await prisma.meetingSeries
      .deleteMany({ where: { id: { in: [OLD_FFAI_SERIES_ID, NEW_FFAI_SERIES_ID] } } })
      .catch(() => undefined);
  }

  beforeEach(async () => {
    // 防止上一轮失败遗留(afterEach 未执行)
    await purgeLegacyFixtures();
  });

  afterEach(async () => {
    jest.restoreAllMocks();
    await purgeLegacyFixtures();
    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: `legacy_admin_${suffix}`,
      email: `legacy_admin_${suffix}@example.com`,
      password: 'Admin@123',
      displayName: 'Legacy 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 };
  }

  /**
   * 种子数据布局:
   *   OLD_FFAI 系列:
   *     - 3/09 meeting (旧无,不创建)
   *     - 3/16 meeting (1 条实签 ON_SITE) ← 合并时以此为准
   *   NEW_FFAI 系列:
   *     - 3/09 meeting (1 条实签 ONLINE)
   *     - 3/16 meeting (1 条 NOT_CHECKED_IN 占位) ← 合并视图会排除
   *     - 3/23 meeting (1 条实签 ONLINE)
   */
  async function seedLegacyAndNewSeries(creatorId: string) {
    await prisma.meetingSeries.create({
      data: {
        id: OLD_FFAI_SERIES_ID,
        title: 'Old FFAI EC Meeting',
        pattern: 'WEEKLY',
        frequency: 1,
        startDate: new Date('2025-11-01T00:00:00Z'),
        endDate: new Date('2026-03-17T00:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        creatorId,
        isActive: true,
      } as any,
    });
    await prisma.meetingSeries.create({
      data: {
        id: NEW_FFAI_SERIES_ID,
        title: '[Video Conference Call] FFAI EC Meeting',
        pattern: 'WEEKLY',
        frequency: 1,
        startDate: new Date('2026-03-01T00:00:00Z'),
        endDate: new Date('2027-01-01T00:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        creatorId,
        isActive: true,
      } as any,
    });

    const oldUser = await createTestUser({
      username: `old_signer_${Date.now()}`,
      email: `old_signer_${Date.now()}@example.com`,
      displayName: 'Old Signer',
    } as any);
    const newUser = await createTestUser({
      username: `new_signer_${Date.now() + 1}`,
      email: `new_signer_${Date.now() + 1}@example.com`,
      displayName: 'New Signer',
    } as any);

    async function makeMeeting(
      seriesId: string,
      startTime: Date,
      suffix: string,
      attendUser: { id: string },
      attendStatus: 'ON_SITE' | 'ONLINE' | 'NOT_CHECKED_IN',
    ) {
      const meeting = await prisma.meeting.create({
        data: {
          title: `Meeting ${suffix}`,
          startTime,
          endTime: new Date(startTime.getTime() + 60 * 60 * 1000),
          timezone: 'UTC',
          type: 'HYBRID',
          status: 'COMPLETED',
          creatorId,
          seriesId,
        } as any,
      });
      await prisma.meetingRequiredAttendee.create({
        data: { meetingId: meeting.id, userId: attendUser.id, role: 'REGULAR_ATTENDEE' } as any,
      });
      await prisma.meetingAttendance.create({
        data: {
          meetingId: meeting.id,
          userId: attendUser.id,
          status: attendStatus,
          checkinTime: attendStatus === 'NOT_CHECKED_IN' ? null : startTime,
        } as any,
      });
      return meeting;
    }

    await makeMeeting(OLD_FFAI_SERIES_ID, new Date('2026-03-16T15:30:00Z'), 'old-0316', oldUser, 'ON_SITE');
    await makeMeeting(NEW_FFAI_SERIES_ID, new Date('2026-03-09T15:30:00Z'), 'new-0309', newUser, 'ONLINE');
    await makeMeeting(NEW_FFAI_SERIES_ID, new Date('2026-03-16T15:30:00Z'), 'new-0316', newUser, 'NOT_CHECKED_IN');
    await makeMeeting(NEW_FFAI_SERIES_ID, new Date('2026-03-23T15:30:00Z'), 'new-0323', newUser, 'ONLINE');

    return { oldUser, newUser };
  }

  test('1. GET /reports/series-options 返回 hasLegacy 标记', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    await seedLegacyAndNewSeries(adminUser.id);

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

    const opts = resp.body as Array<{ id: string; hasLegacy: boolean }>;
    const newEntry = opts.find((o) => o.id === NEW_FFAI_SERIES_ID);
    const oldEntry = opts.find((o) => o.id === OLD_FFAI_SERIES_ID);

    expect(newEntry?.hasLegacy).toBe(true);
    expect(oldEntry?.hasLegacy).toBe(false);
  });

  test('2. getSeriesReport includeLegacy=0 → 仅新系列 meeting', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    await seedLegacyAndNewSeries(adminUser.id);

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

    expect(resp.body.legacyIncluded).toEqual([]);
    expect(resp.body.overallStats.totalMeetings).toBe(3); // 3/09 + 3/16 + 3/23
    expect(resp.body.overallStats.totalAttended).toBe(2); // 3/09 ONLINE + 3/23 ONLINE (3/16 NOT_CHECKED_IN 不算)
    expect(resp.body.hasLegacy).toBe(true);
  });

  test('3. getSeriesReport includeLegacy=1 → 合并,3/16 取旧,3/09 和 3/23 取新', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    await seedLegacyAndNewSeries(adminUser.id);

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

    expect(resp.body.legacyIncluded).toEqual([OLD_FFAI_SERIES_ID]);
    // 合并后:旧 3/16 + 新 3/09 + 新 3/23 = 3 场(新 3/16 被排除)
    expect(resp.body.overallStats.totalMeetings).toBe(3);
    // 实到数:旧 3/16 ON_SITE + 新 3/09 ONLINE + 新 3/23 ONLINE = 3
    expect(resp.body.overallStats.totalAttended).toBe(3);

    // meetingRange.firstMeeting 应该是 3/09 (时间最早)
    expect(resp.body.meetingRange.firstMeeting?.startTime).toContain('2026-03-09');
  });

  test('4. effectiveStartTime 等于合并后首场 meeting 的 startTime', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    await seedLegacyAndNewSeries(adminUser.id);

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

    // 合并后最早是 3/09 (新系列),不是 NEW_FFAI 的 startDate (2026-03-01)
    expect(resp.body.series.effectiveStartTime).toContain('2026-03-09');
    expect(resp.body.series.startDate).toContain('2026-03-01');
  });

  test('5. includeLegacy=1 确认新系列 3/16 被排除(合并视图不双算)', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    await seedLegacyAndNewSeries(adminUser.id);

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

    // 如果没排除新 3/16,实到数 = 3(旧 3/16 ON_SITE + 新 3/09 + 新 3/23)
    // 没排除的话会变成 3(NOT_CHECKED_IN 不计入 attended),但 totalMeetings 会是 4
    // 通过 totalMeetings 区分
    expect(resp.body.overallStats.totalMeetings).toBe(3);
    // 对比:不开 includeLegacy,totalMeetings 是 3(新 3/09+3/16+3/23)
    // 开 includeLegacy,totalMeetings 也是 3(旧 3/16 替代 新 3/16)
    // 如果逻辑错误两边加合,会是 4
  });
});
