import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { METER_KINDS, type MeterReadingCreate, type MetersResponse } from '@siop/shared';
import type { AuthenticatedUser } from '../auth/current-user.decorator';
import { PrismaService } from '../prisma/prisma.service';

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

  async list(assetId: string): Promise<MetersResponse> {
    await this.assertAsset(assetId);
    const meters = await this.prisma.meter.findMany({
      where: { assetId },
      include: {
        readings: {
          include: { readBy: true },
          orderBy: { createdAt: 'desc' },
          take: 10,
        },
      },
    });
    // Les deux compteurs existent toujours dans la réponse, même vides
    return {
      meters: METER_KINDS.map((kind) => {
        const meter = meters.find((m) => m.kind === kind);
        return {
          kind,
          readings:
            meter?.readings.map((r) => ({
              id: r.id,
              value: r.value,
              readBy: r.readBy
                ? { id: r.readBy.id, displayName: r.readBy.displayName }
                : null,
              createdAt: r.createdAt.toISOString(),
            })) ?? [],
        };
      }),
    };
  }

  /** Invariant : un compteur ne redescend jamais. */
  async addReading(
    assetId: string,
    dto: MeterReadingCreate,
    user: AuthenticatedUser,
  ): Promise<MetersResponse> {
    await this.assertAsset(assetId);
    const meter = await this.prisma.meter.upsert({
      where: { assetId_kind: { assetId, kind: dto.kind } },
      update: {},
      create: { assetId, kind: dto.kind },
    });
    const dernier = await this.prisma.meterReading.findFirst({
      where: { meterId: meter.id },
      orderBy: { createdAt: 'desc' },
    });
    if (dernier && dto.value <= dernier.value) {
      throw new BadRequestException(
        `Relevé refusé : ${dto.value} est inférieur ou égal au précédent (${dernier.value}) — un compteur ne redescend pas`,
      );
    }
    await this.prisma.meterReading.create({
      data: { meterId: meter.id, value: dto.value, readById: user.userId },
    });
    return this.list(assetId);
  }

  private async assertAsset(assetId: string): Promise<void> {
    const asset = await this.prisma.asset.findUnique({ where: { id: assetId } });
    if (!asset) throw new NotFoundException('Appareil inconnu');
  }
}
