import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Not } from 'typeorm';
import { Room } from '../entities/room.entity';
import { Bed } from '../entities/bed.entity';
import { Locker } from '../entities/locker.entity';
import type { CreateBedDto, UpdateBedDto, BatchCreateBedDto } from './dto/bed.dto';
import type { CreateLockerDto, UpdateLockerDto, BatchCreateLockerDto } from './dto/locker.dto';

@Injectable()
export class RoomBedLockerService {
  constructor(
    @InjectRepository(Room) private repo: Repository<Room>,
    @InjectRepository(Bed) private bedRepo: Repository<Bed>,
    @InjectRepository(Locker) private lockerRepo: Repository<Locker>,
  ) {}

  async getRoomBeds(roomId: number): Promise<Bed[]> {
    const room = await this.repo.findOne({ where: { id: roomId } });
    if (!room) throw new NotFoundException('宿舍不存在');
    return this.bedRepo.find({
      where: { roomId, status: Not('archived') },
      order: { bedNumber: 'ASC' },
    });
  }

  async getRoomAvailableBeds(roomId: number): Promise<Bed[]> {
    const room = await this.repo.findOne({ where: { id: roomId } });
    if (!room) throw new NotFoundException('宿舍不存在');
    return this.bedRepo.find({
      where: { roomId, status: 'available' },
      order: { bedNumber: 'ASC' },
    });
  }

  async createBed(roomId: number, dto: CreateBedDto): Promise<Bed> {
    const room = await this.repo.findOne({ where: { id: roomId } });
    if (!room) throw new NotFoundException('宿舍不存在');
    if (room.status === 'archived') throw new BadRequestException('已归档宿舍不能添加床位');
    await this.assertCanAddBeds(room, 1);
    const existing = await this.bedRepo.findOne({ where: { roomId, bedNumber: dto.bedNumber } });
    if (existing) throw new BadRequestException('该床位编号已存在');
    const bed = this.bedRepo.create({ ...dto, roomId });
    return this.bedRepo.save(bed);
  }

  async updateBed(roomId: number, id: number, dto: UpdateBedDto): Promise<Bed> {
    const bed = await this.bedRepo.findOne({ where: { id, roomId } });
    if (!bed) throw new NotFoundException('床位不存在');
    // 不允许将 occupied 的床位改为 maintenance
    if (dto.status === 'maintenance' && bed.status === 'occupied') {
      throw new BadRequestException('该床位有人入住，请先退宿');
    }
    // 编号唯一性检查
    if (dto.bedNumber && dto.bedNumber !== bed.bedNumber) {
      const dup = await this.bedRepo.findOne({ where: { roomId, bedNumber: dto.bedNumber } });
      if (dup) throw new BadRequestException('该床位编号已存在');
    }
    Object.assign(bed, dto);
    return this.bedRepo.save(bed);
  }

  async deleteBed(roomId: number, id: number): Promise<void> {
    const bed = await this.bedRepo.findOne({ where: { id, roomId } });
    if (!bed) throw new NotFoundException('床位不存在');
    if (bed.status === 'occupied') throw new BadRequestException('该床位有人入住，无法归档');
    if (bed.status === 'archived') throw new BadRequestException('该床位已归档');
    await this.bedRepo.update(id, { status: 'archived' });
  }

  async batchCreateBeds(roomId: number, dto: BatchCreateBedDto): Promise<Bed[]> {
    const room = await this.repo.findOne({ where: { id: roomId } });
    if (!room) throw new NotFoundException('宿舍不存在');
    if (room.status === 'archived') throw new BadRequestException('已归档宿舍不能添加床位');
    const existing = await this.bedRepo.find({
      where: { roomId, status: Not('archived') },
      order: { bedNumber: 'ASC' },
    });
    this.assertCanAddBedsFromCount(room, existing.length, dto.count);
    const numbers = existing.map((b) => {
      const match = b.bedNumber.match(/^\d+/);
      return match ? parseInt(match[0]) : 0;
    });
    const start = numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
    const beds: Bed[] = [];
    for (let i = 0; i < dto.count; i++) {
      beds.push(this.bedRepo.create({ roomId, bedNumber: `${start + i}号床` }));
    }
    return this.bedRepo.save(beds);
  }


  getNextBedNumber(beds: Pick<Bed, 'bedNumber'>[]): number {
    const numbers = beds.map((bed) => {
      const match = bed.bedNumber.match(/^\d+/);
      return match ? parseInt(match[0], 10) : 0;
    });
    return numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
  }

  private async assertCanAddBeds(room: Room, count: number): Promise<void> {
    const existingCount = await this.bedRepo.count({ where: { roomId: room.id } });
    this.assertCanAddBedsFromCount(room, existingCount, count);
  }

  private assertCanAddBedsFromCount(room: Room, existingCount: number, count: number): void {
    const remaining = Math.max((room.capacity ?? 0) - existingCount, 0);
    if (count > remaining) {
      throw new BadRequestException(
        `床位不能超过额定人数，当前已有 ${existingCount} 张，额定 ${room.capacity} 张，最多还能添加 ${remaining} 张`,
      );
    }
  }

  // ── 柜子管理 ──

  async getRoomLockers(roomId: number): Promise<Locker[]> {
    const room = await this.repo.findOne({ where: { id: roomId } });
    if (!room) throw new NotFoundException('宿舍不存在');
    return this.lockerRepo.find({
      where: { roomId, status: Not('archived') },
      order: { lockerNumber: 'ASC' },
    });
  }

  async getRoomAvailableLockers(roomId: number): Promise<Locker[]> {
    const room = await this.repo.findOne({ where: { id: roomId } });
    if (!room) throw new NotFoundException('宿舍不存在');
    return this.lockerRepo.find({
      where: { roomId, status: 'available' },
      order: { lockerNumber: 'ASC' },
    });
  }

  async createLocker(roomId: number, dto: CreateLockerDto): Promise<Locker> {
    const room = await this.repo.findOne({ where: { id: roomId } });
    if (!room) throw new NotFoundException('宿舍不存在');
    if (room.status === 'archived') throw new BadRequestException('已归档宿舍不能添加柜子');
    const existing = await this.lockerRepo.findOne({
      where: { roomId, lockerNumber: dto.lockerNumber },
    });
    if (existing) throw new BadRequestException('该柜子编号已存在');
    const locker = this.lockerRepo.create({ ...dto, roomId });
    return this.lockerRepo.save(locker);
  }

  async updateLocker(roomId: number, id: number, dto: UpdateLockerDto): Promise<Locker> {
    const locker = await this.lockerRepo.findOne({ where: { id, roomId } });
    if (!locker) throw new NotFoundException('柜子不存在');
    if (dto.status === 'maintenance' && locker.status === 'occupied') {
      throw new BadRequestException('该柜子有人占用，请先释放');
    }
    if (dto.lockerNumber && dto.lockerNumber !== locker.lockerNumber) {
      const dup = await this.lockerRepo.findOne({
        where: { roomId, lockerNumber: dto.lockerNumber },
      });
      if (dup) throw new BadRequestException('该柜子编号已存在');
    }
    Object.assign(locker, dto);
    return this.lockerRepo.save(locker);
  }

  async deleteLocker(roomId: number, id: number): Promise<void> {
    const locker = await this.lockerRepo.findOne({ where: { id, roomId } });
    if (!locker) throw new NotFoundException('柜子不存在');
    if (locker.status === 'occupied') throw new BadRequestException('该柜子有人占用，无法归档');
    if (locker.status === 'archived') throw new BadRequestException('该柜子已归档');
    await this.lockerRepo.update(id, { status: 'archived' });
  }

  async batchCreateLockers(roomId: number, dto: BatchCreateLockerDto): Promise<Locker[]> {
    const room = await this.repo.findOne({ where: { id: roomId } });
    if (!room) throw new NotFoundException('宿舍不存在');
    if (room.status === 'archived') throw new BadRequestException('已归档宿舍不能添加柜子');
    const existing = await this.lockerRepo.find({
      where: { roomId, status: Not('archived') },
      order: { lockerNumber: 'ASC' },
    });
    const numbers = existing.map((b) => {
      const match = b.lockerNumber.match(/^\d+/);
      return match ? parseInt(match[0]) : 0;
    });
    const start = numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
    const lockers: Locker[] = [];
    for (let i = 0; i < dto.count; i++) {
      lockers.push(this.lockerRepo.create({ roomId, lockerNumber: `${start + i}号柜` }));
    }
    return this.lockerRepo.save(lockers);
  }
}
