import { Injectable, Logger } from '@nestjs/common';
import { Device, Prisma } from '@prisma/client';
import { DevicePositionSnapshot } from '../devices/device-position';
import { PrismaService } from '../prisma/prisma.service';
import { RealtimeService } from '../realtime/realtime.service';

@Injectable()
export class LocatorRegistryService {
  private readonly logger = new Logger(LocatorRegistryService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly realtime: RealtimeService,
  ) {}

  // Serial numbers are globally unique, so a bare serial identifies a locator
  // regardless of which org's data pipeline saw it first. Unknown serials are
  // auto-registered under the given org so field data is never dropped.
  async resolve(orgId: string, serial: string): Promise<Device> {
    const existing = await this.prisma.device.findUnique({ where: { serialNumber: serial } });
    if (existing) {
      this.prisma.device
        .update({ where: { id: existing.id }, data: { lastSeenAt: new Date() } })
        .catch(() => undefined);
      return existing;
    }

    this.logger.log(`Auto-registering locator serial ${serial} under org ${orgId}`);
    try {
      return await this.prisma.device.create({
        data: { orgId, name: `Locator ${serial}`, serialNumber: serial, lastSeenAt: new Date() },
      });
    } catch {
      // lost a concurrent-registration race; the row exists now
      const raced = await this.prisma.device.findUnique({ where: { serialNumber: serial } });
      if (!raced) {
        throw new Error(`Failed to resolve or create locator ${serial}`);
      }
      return raced;
    }
  }

  // Records the freshest known position for a device (whether from a live
  // "status" ping or a persisted "log" point) and broadcasts it to anyone
  // watching the org's devices page, so it updates without a manual refresh.
  async recordPosition(orgId: string, deviceId: string, snapshot: DevicePositionSnapshot) {
    const lastSeenAt = new Date();
    await this.prisma.device
      .update({
        where: { id: deviceId },
        data: {
          lastPosition: snapshot as unknown as Prisma.InputJsonValue,
          lastPositionAt: new Date(snapshot.recordedAt),
          lastSeenAt,
        },
      })
      .catch(() => undefined);

    this.realtime.publish(`org:${orgId}:devices`, {
      type: 'device',
      orgId,
      deviceId,
      lastSeenAt: lastSeenAt.toISOString(),
      position: {
        id: `live-${deviceId}`,
        lat: snapshot.lat,
        lng: snapshot.lng,
        altitude: snapshot.altitude,
        utilityType: snapshot.utilityType,
        fixType: snapshot.fixType,
        sequence: null,
        recordedAt: snapshot.recordedAt,
        hAccuracy: snapshot.hAccuracy,
        vAccuracy: snapshot.vAccuracy,
        satellites: snapshot.satellites,
        hdop: snapshot.hdop,
        depth: snapshot.depth,
        frequencyHz: snapshot.frequencyHz,
        currentMa: snapshot.currentMa,
        signalDb: snapshot.signalDb,
        gainDb: snapshot.gainDb,
        locateMode: snapshot.locateMode,
        phaseDeg: snapshot.phaseDeg,
        compassDeg: snapshot.compassDeg,
        distortionPct: snapshot.distortionPct,
      },
      job: { id: snapshot.jobId, ticketNumber: snapshot.jobTicketNumber, title: snapshot.jobTitle },
    });
  }
}
