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

/**
 * v1.2 签到方式校验集成测试 - 核心 8 条用例
 *
 * 覆盖：
 *   1. 开关 guard（city 空时拒开）
 *   2. listRequiredAttendees enrichment - 会议级 override (MEETING_OVERRIDE)
 *   3. listRequiredAttendees enrichment - 系列级 preference (SERIES_PREFERENCE)
 *   4. listRequiredAttendees enrichment - 城市派生 workCity == meeting.city → ON_SITE
 *   5. listRequiredAttendees enrichment - 城市派生 workCity != meeting.city → ONLINE
 *   6. listRequiredAttendees enrichment - workCity 空 → allowedMode null
 *   7. 级联刷新：PATCH series enforce-checkin-mode 同步下属 meeting
 *   8. 签到 qrType 校验 - qrType 与 allowed 不符 → 400 + code MEETING_ATTENDANCE_033
 */
describe('v1.2 Checkin Mode Validation Integration Tests', () => {
  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: `v12_admin_${suffix}`,
      email: `v12_admin_${suffix}@example.com`,
      password: 'Admin@123',
      displayName: 'v1.2 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 createMeetingWithAttendee(opts: {
    city?: string | null;
    enforceCheckinMode?: boolean;
    userWorkCity?: string | null;
    attendeeCheckinMode?: 'ON_SITE' | 'ONLINE' | null;
    seriesId?: string | null;
    creatorId: string;
  }) {
    const suffix = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const user = await createTestUser({
      username: `v12_user_${suffix}`,
      email: `v12_user_${suffix}@example.com`,
      displayName: `v1.2 User ${suffix}`,
      workCity: opts.userWorkCity ?? null,
    } as any);

    const meeting = await prisma.meeting.create({
      data: {
        title: `v1.2 Meeting ${suffix}`,
        startTime: new Date('2026-06-01T10:00:00Z'),
        endTime: new Date('2026-06-01T11:00:00Z'),
        timezone: 'UTC',
        location: 'HQ',
        type: 'HYBRID',
        status: 'SCHEDULED',
        creatorId: opts.creatorId,
        seriesId: opts.seriesId ?? null,
        city: opts.city ?? null,
        enforceCheckinMode: opts.enforceCheckinMode ?? false,
      } as any,
    });

    await prisma.meetingRequiredAttendee.create({
      data: {
        meetingId: meeting.id,
        userId: user.id,
        role: 'REGULAR_ATTENDEE',
        checkinMode: opts.attendeeCheckinMode ?? null,
      } as any,
    });

    return { meeting, user };
  }

  it('[1] 开启 enforceCheckinMode 前必须先设 city，否则 400', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const { meeting } = await createMeetingWithAttendee({
      creatorId: adminUser.id,
      city: null,
    });

    const resp = await request(app.getHttpServer())
      .patch(`/api/v1/meeting-attendance/meetings/${meeting.id}/enforce-checkin-mode`)
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ enforceCheckinMode: true });

    expect(resp.status).toBe(400);
  });

  it('[2] listRequiredAttendees: 会议级 override 生效 → MEETING_OVERRIDE', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const { meeting } = await createMeetingWithAttendee({
      creatorId: adminUser.id,
      city: 'Beijing',
      enforceCheckinMode: true,
      userWorkCity: 'Shanghai',
      attendeeCheckinMode: 'ON_SITE',
    });

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

    const attendees = resp.body.requiredAttendees;
    expect(attendees).toHaveLength(1);
    expect(attendees[0].allowedMode).toBe('ON_SITE');
    expect(attendees[0].allowedModeSource).toBe('MEETING_OVERRIDE');
    expect(attendees[0].isOverridden).toBe(true);
    expect(attendees[0].defaultMode).toBe('ONLINE');
  });

  it('[3] listRequiredAttendees: 系列级 preference 生效 → SERIES_PREFERENCE', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const suffix = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const series = await prisma.meetingSeries.create({
      data: {
        title: `Series ${suffix}`,
        pattern: 'WEEKLY',
        frequency: 1,
        startDate: new Date('2026-05-01T00:00:00Z'),
        endDate: new Date('2026-07-01T00:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        creatorId: adminUser.id,
        isActive: true,
        city: 'Beijing',
        enforceCheckinMode: true,
      } as any,
    });

    const { meeting, user } = await createMeetingWithAttendee({
      creatorId: adminUser.id,
      seriesId: series.id,
      city: 'Beijing',
      enforceCheckinMode: true,
      userWorkCity: 'Shanghai',
    });

    await (prisma as any).meetingSeriesAttendeePreference.create({
      data: {
        seriesId: series.id,
        userId: user.id,
        defaultCheckinMode: 'ON_SITE',
      },
    });

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

    const a = resp.body.requiredAttendees[0];
    expect(a.allowedMode).toBe('ON_SITE');
    expect(a.allowedModeSource).toBe('SERIES_PREFERENCE');
    expect(a.isOverridden).toBe(false);
  });

  it('[4] listRequiredAttendees: 城市派生 workCity == meeting.city → ON_SITE', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const { meeting } = await createMeetingWithAttendee({
      creatorId: adminUser.id,
      city: 'Beijing',
      enforceCheckinMode: true,
      userWorkCity: 'Beijing',
    });

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

    const a = resp.body.requiredAttendees[0];
    expect(a.allowedMode).toBe('ON_SITE');
    expect(a.allowedModeSource).toBe('CITY_DERIVED');
    expect(a.workCity).toBe('Beijing');
  });

  it('[5] listRequiredAttendees: 城市派生 workCity != meeting.city → ONLINE', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const { meeting } = await createMeetingWithAttendee({
      creatorId: adminUser.id,
      city: 'Beijing',
      enforceCheckinMode: true,
      userWorkCity: 'Shanghai',
    });

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

    const a = resp.body.requiredAttendees[0];
    expect(a.allowedMode).toBe('ONLINE');
    expect(a.allowedModeSource).toBe('CITY_DERIVED');
  });

  it('[6] listRequiredAttendees: 用户 workCity 空 → allowedMode null', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const { meeting } = await createMeetingWithAttendee({
      creatorId: adminUser.id,
      city: 'Beijing',
      enforceCheckinMode: true,
      userWorkCity: null,
    });

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

    const a = resp.body.requiredAttendees[0];
    expect(a.allowedMode).toBeNull();
    expect(a.allowedModeSource).toBe('CITY_DERIVED');
  });

  it('[7] 级联刷新：PATCH series enforce-checkin-mode 同步下属 meeting', async () => {
    const { adminUser, adminToken } = await loginAsAdmin();
    const suffix = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const series = await prisma.meetingSeries.create({
      data: {
        title: `Cascade Series ${suffix}`,
        pattern: 'WEEKLY',
        frequency: 1,
        startDate: new Date('2026-05-01T00:00:00Z'),
        endDate: new Date('2026-07-01T00:00:00Z'),
        timezone: 'UTC',
        type: 'HYBRID',
        creatorId: adminUser.id,
        isActive: true,
        city: 'Beijing',
        enforceCheckinMode: false,
      } as any,
    });

    const { meeting } = await createMeetingWithAttendee({
      creatorId: adminUser.id,
      seriesId: series.id,
      city: 'Beijing',
      enforceCheckinMode: false,
    });

    await request(app.getHttpServer())
      .patch(`/api/v1/meeting-attendance/series/${series.id}/enforce-checkin-mode`)
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ enforceCheckinMode: true })
      .expect(200);

    const refreshed = await prisma.meeting.findUnique({ where: { id: meeting.id } });
    expect((refreshed as any).enforceCheckinMode).toBe(true);
  });

  it('[8] 签到 qrType 与 allowed 不符 → 400 + code MEETING_ATTENDANCE_033', async () => {
    const { adminUser } = await loginAsAdmin();
    const { meeting, user } = await createMeetingWithAttendee({
      creatorId: adminUser.id,
      city: 'Beijing',
      enforceCheckinMode: true,
      userWorkCity: 'Beijing', // 派生 ON_SITE
    });

    await prisma.meeting.update({
      where: { id: meeting.id },
      data: { status: 'IN_PROGRESS', startTime: new Date(Date.now() - 60_000) },
    });

    const resp = await request(app.getHttpServer())
      .post(`/api/v1/meeting-attendance/meetings/${meeting.id}/guest-checkin`)
      .send({
        name: user.displayName,
        email: user.email,
        qrType: 'online',
      });

    expect(resp.status).toBe(400);
    expect(resp.body.code).toBe('MEETING_ATTENDANCE_033');
  });
});
