import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Not, In } from 'typeorm';
import { Room } from '../entities/room.entity';
import { Occupancy } from '../entities/occupancy.entity';
import { Bed } from '../entities/bed.entity';
import { RoomInspectionsService } from './room-inspections.service';
import { occupancyWhereOnDate } from './room-occupancy-date';
import { parseRoomNumber } from './room-number';

@Injectable()
export class RoomQueryService {
  constructor(
    @InjectRepository(Room) private repo: Repository<Room>,
    @InjectRepository(Occupancy) private occRepo: Repository<Occupancy>,
    @InjectRepository(Bed) private bedRepo: Repository<Bed>,
    private readonly inspectionsService: RoomInspectionsService,
  ) {}



  async agentSearchRooms(query: { building?: string; keyword?: string; status?: string; limit?: number }) {
    const qb = this.repo.createQueryBuilder('room');
    if (query.building) qb.andWhere('room.building = :building', { building: query.building });
    if (query.keyword) {
      qb.andWhere('(room.roomNumber LIKE :keyword OR room.building LIKE :keyword)', {
        keyword: `%${query.keyword}%`,
      });
    }
    if (query.status) qb.andWhere('room.status = :status', { status: query.status });
    const rows = await qb
      .select([
        'room.id',
        'room.roomNumber',
        'room.building',
        'room.floor',
        'room.capacity',
        'room.roomType',
        'room.status',
      ])
      .orderBy('room.roomNumber', 'ASC')
      .limit(Math.max(1, Math.min(query.limit ?? 20, 50)))
      .getRawMany();
    return rows.map((row) => ({
      id: Number(row.room_id),
      roomNumber: String(row.room_room_number),
      building: row.room_building == null ? null : String(row.room_building),
      floor: row.room_floor == null ? null : Number(row.room_floor),
      capacity: Number(row.room_capacity),
      roomType: row.room_room_type == null ? null : String(row.room_room_type),
      status: String(row.room_status),
    }));
  }

  async agentGetRoomOccupancySummary(query: { date?: string; building?: string; limit?: number }) {
    const targetDate = query.date || this.getChinaDate(new Date());
    const qb = this.occRepo
      .createQueryBuilder('o')
      .innerJoin('o.room', 'room')
      .where('o.checkInDate <= :date', { date: targetDate })
      .andWhere('(o.checkOutDate IS NULL OR o.checkOutDate >= :date)', { date: targetDate });
    if (query.building) qb.andWhere('room.building = :building', { building: query.building });
    const rows = await qb
      .select('room.id', 'roomId')
      .addSelect('room.roomNumber', 'roomNumber')
      .addSelect('COUNT(o.id)', 'occupied')
      .addSelect('room.capacity', 'capacity')
      .groupBy('room.id')
      .orderBy('room.roomNumber', 'ASC')
      .limit(Math.max(1, Math.min(query.limit ?? 20, 50)))
      .getRawMany();
    return rows.map((row) => ({
      roomId: Number(row.roomId),
      roomNumber: String(row.roomNumber),
      occupied: Number(row.occupied),
      capacity: Number(row.capacity),
      rate: Number(row.capacity) > 0 ? Number(((Number(row.occupied) / Number(row.capacity)) * 100).toFixed(1)) : 0,
    }));
  }

  async getRoomVisual(asOf?: string) {
    // asOf 为空 = 实时（今天）。带 asOf = 还原该日期结束时的历史入住快照。
    const isHistorical = !!asOf;
    const targetDate = asOf || this.getChinaDate(new Date());

    // 实时视图排除已归档房间；历史视图不排除——当时有人住的房间即使现在已归档也应显示。
    const rooms = await this.repo.find({
      where: isHistorical ? {} : { status: Not('archived') },
      order: { building: 'ASC', roomNumber: 'ASC' },
    });

    const occupancies = await this.occRepo.find({
      where: occupancyWhereOnDate(targetDate),
      relations: ['student', 'student.organization', 'responsibleOrganization', 'bed'],
      order: { checkInDate: 'ASC' },
    });

    // 按roomId分组入住记录
    const occMap = new Map<number, any[]>();
    // days（已住天数）相对目标日期计算，而非固定今天，历史视图才准确。
    const refTime = new Date(targetDate).getTime();
    for (const occ of occupancies) {
      if (!occMap.has(occ.roomId)) occMap.set(occ.roomId, []);
      const checkIn = new Date(occ.checkInDate);
      const days = Math.max(1, Math.ceil((refTime - checkIn.getTime()) / (1000 * 60 * 60 * 24)));
      occMap.get(occ.roomId)!.push({
        studentId: occ.studentId,
        occupancyId: occ.id,
        studentName: occ.student?.name || '未知',
        bedId: occ.bedId ?? null,
        bedNumber: occ.bed?.bedNumber || null,
        checkInDate: occ.checkInDate,
        billingStartDate: occ.billingStartDate,
        days,
        organization: occ.student?.organization?.name || null,
        supervisor: occ.student?.supervisor || null,
        organizationId: occ.responsibleOrganizationId || null,
        organizationName: occ.responsibleOrganization?.name || null,
        organizationColor: occ.responsibleOrganization?.color || null,
      });
    }

    // 获取各楼栋列表
    const buildings = [...new Set(rooms.map((r) => r.building).filter(Boolean))];

    // 历史视图纳入了已归档房间，但只保留当时确实有人住的归档房间，避免空归档房间刷屏。
    const visibleRooms = isHistorical
      ? rooms.filter((r) => r.status !== 'archived' || (occMap.get(r.id)?.length ?? 0) > 0)
      : rooms;

    // 批量获取床位统计
    const allBeds = await this.bedRepo.find({
      where: { roomId: In(visibleRooms.map((r) => r.id)) },
    });
    const bedMap = new Map<number, { total: number; occupied: number }>();
    for (const bed of allBeds) {
      if (!bedMap.has(bed.roomId)) bedMap.set(bed.roomId, { total: 0, occupied: 0 });
      const entry = bedMap.get(bed.roomId)!;
      entry.total++;
      if (bed.status === 'occupied') entry.occupied++;
    }

    const inspectionMap = await this.inspectionsService.getByRoomsAndDate(
      visibleRooms.map((room) => room.id),
      targetDate,
    );

    return {
      buildings,
      rooms: visibleRooms.map((room) => {
        const occ = occMap.get(room.id) || [];
        const inspection = inspectionMap.get(room.id);
        const inspectionByOccupancyId = new Map(
          (inspection?.details || []).map((detail) => [detail.occupancyId, detail.status]),
        );
        const orgs = [...new Set(occ.map((o: any) => o.organization).filter(Boolean))];
        let orgLabel: string | null = null;
        if (orgs.length > 0 && occ.length > 0) {
          const allSameOrg = occ.every((o: any) => o.organization && o.organization === orgs[0]);
          orgLabel = allSameOrg ? `均为${orgs[0]}人员` : `存在${orgs.join('、')}人员`;
        }
        const organizationColors = [
          ...new Set(occ.map((o: any) => o.organizationColor).filter(Boolean)),
        ];
        const organizationColor: string | null =
          organizationColors.length === 1 ? organizationColors[0] : null;
        const organizationIds = [...new Set(occ.map((o: any) => o.organizationId).filter(Boolean))];
        return {
          id: room.id,
          roomNumber: room.roomNumber,
          building: room.building,
          floor: room.floor,
          capacity: room.capacity,
          status: room.status,
          currentCount: occ.length,
          totalBeds: bedMap.get(room.id)?.total ?? 0,
          occupiedBeds: bedMap.get(room.id)?.occupied ?? 0,
          occupants: occ.map((occupant) => ({
            ...occupant,
            inspectionStatus: inspectionByOccupancyId.get(occupant.occupancyId) || null,
          })),
          inspection: inspection
            ? {
                submitted: true,
                inspectorId: inspection.inspectorId,
                inspectorName: inspection.inspectorName,
                source: inspection.source,
                submittedAt: inspection.submittedAt,
              }
            : { submitted: false },
          orgLabel,
          organizationColor,
          organizationIds,
        };
      }),
      // 当前视图内出现过的负责机构，供筛选下拉使用
      organizations: [
        ...new Map(
          occupancies
            .filter((o) => o.responsibleOrganizationId && o.responsibleOrganization)
            .map((o) => [
              o.responsibleOrganizationId,
              {
                id: o.responsibleOrganizationId,
                name: o.responsibleOrganization.name,
                color: o.responsibleOrganization.color || null,
              },
            ]),
        ).values(),
      ].sort((a, b) => a.name.localeCompare(b.name)),
    };
  }

  private getChinaDate(now: Date): string {
    return new Intl.DateTimeFormat('en-CA', {
      timeZone: 'Asia/Shanghai',
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
    }).format(now);
  }


  async batchImport(
    rows: {
      roomNumber: string;
      building?: string;
      floor?: number;
      capacity?: number;
      roomType?: string;
      rentalCategory?: string;
      monthlyRate?: number;
    }[],
  ) {
    let imported = 0;
    let skipped = 0;
    for (const row of rows) {
      if (!row.roomNumber || !row.roomNumber.trim()) {
        skipped++;
        continue;
      }
      const exists = await this.repo.findOne({ where: { roomNumber: row.roomNumber.trim() } });
      if (exists) {
        skipped++;
        continue;
      }
      // 智能解析房间号
      const parsed = parseRoomNumber(row.roomNumber.trim());
      const room = await this.repo.save(
        this.repo.create({
          roomNumber: row.roomNumber.trim(),
          building: row.building?.trim() || parsed.building || undefined,
          floor: row.floor ?? parsed.floor,
          capacity: row.capacity ?? parsed.capacity ?? 4,
          roomType: row.roomType || parsed.roomType || undefined,
          rentalCategory: row.rentalCategory || undefined,
          monthlyRate: row.monthlyRate ?? undefined,
        }),
      );
      await this.createDefaultBeds(room.id, room.capacity);
      imported++;
    }
    return {
      message: `成功导入 ${imported} 间宿舍，跳过 ${skipped} 条（重复或空行）`,
      imported,
      skipped,
    };
  }

  // ── 床位管理 ──


  async createDefaultBeds(roomId: number, capacity: number): Promise<void> {
    const count = Math.max(capacity ?? 0, 0);
    if (count === 0) return;
    const beds = Array.from({ length: count }, (_, index) =>
      this.bedRepo.create({ roomId, bedNumber: `${index + 1}号床` }),
    );
    await this.bedRepo.save(beds);
  }

}
