import {
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';

import { PrismaService } from '@/prisma/prisma.service';

import { CreateMeetingSummaryStatusDto } from './dto/create-meeting-summary-status.dto';
import { UpdateMeetingSummaryStatusDto } from './dto/update-meeting-summary-status.dto';

@Injectable()
export class MeetingSummaryStatusService {
  constructor(private readonly prisma: PrismaService) {}

  // The columns every endpoint returns. Timestamps help an admin screen.
  private readonly selectFields = {
    id: true,
    code: true,
    name: true,
    createdAt: true,
    updatedAt: true,
  };

  // Codes are stored upper-cased so lookups and uniqueness stay predictable.
  private normalizeCode(code: string) {
    return code.trim().toUpperCase();
  }

  async create(dto: CreateMeetingSummaryStatusDto) {
    const code = this.normalizeCode(dto.code);
    await this.ensureCodeIsUnique(code);

    return this.prisma.meetingSummaryStatus.create({
      data: { code, name: dto.name.trim() },
      select: this.selectFields,
    });
  }

  findAll() {
    return this.prisma.meetingSummaryStatus.findMany({
      where: { deletedAt: null },
      orderBy: { id: 'asc' },
      select: this.selectFields,
    });
  }

  async findOne(id: number) {
    const status = await this.prisma.meetingSummaryStatus.findFirst({
      where: { id, deletedAt: null },
      select: this.selectFields,
    });

    if (!status) {
      throw new NotFoundException('Meeting summary status not found');
    }

    return status;
  }

  async update(id: number, dto: UpdateMeetingSummaryStatusDto) {
    await this.findOne(id);

    const data: { code?: string; name?: string } = {};

    if (dto.code !== undefined) {
      const code = this.normalizeCode(dto.code);
      await this.ensureCodeIsUnique(code, id);
      data.code = code;
    }

    if (dto.name !== undefined) {
      data.name = dto.name.trim();
    }

    return this.prisma.meetingSummaryStatus.update({
      where: { id },
      data,
      select: this.selectFields,
    });
  }

  // Soft delete keeps the row, so any meeting summaries still pointing at this
  // status (the FK is required) keep working; it just disappears from the list.
  async remove(id: number) {
    await this.findOne(id);

    await this.prisma.meetingSummaryStatus.update({
      where: { id },
      data: { deletedAt: new Date() },
    });

    return { message: 'Meeting summary status deleted.' };
  }

  // Codes must be unique among active rows. `excludeId` skips the row being
  // updated so re-saving the same code is allowed.
  private async ensureCodeIsUnique(code: string, excludeId?: number) {
    const existing = await this.prisma.meetingSummaryStatus.findFirst({
      where: {
        code,
        deletedAt: null,
        ...(excludeId !== undefined ? { id: { not: excludeId } } : {}),
      },
      select: { id: true },
    });

    if (existing) {
      throw new ConflictException(
        `A meeting summary status with code "${code}" already exists.`,
      );
    }
  }
}
