import { BadRequestException, Injectable } from '@nestjs/common';
import { LampStatus, OnlineStatus, Prisma } from '@prisma/client';
import { AuthUser } from '../../common/current-user.decorator';
import { RegionScopeService } from '../../common/region-scope.service';
import { PrismaService } from '../prisma/prisma.service';

const LAMP_STATUSES: LampStatus[] = ['NORMAL', 'FAULT', 'MAINTENANCE', 'OFFLINE'];
const ONLINE_STATUSES: OnlineStatus[] = ['ONLINE', 'OFFLINE'];

@Injectable()
export class LampIngestionService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly regionScope: RegionScopeService,
  ) {}

  async receive(user: AuthUser, dto: Record<string, unknown>) {
    const code = typeof dto.code === 'string' ? dto.code.trim() : '';
    if (!code) {
      throw new BadRequestException('code 不能为空');
    }

    const existing = await this.prisma.streetLamp.findUnique({ where: { code } });
    if (existing) {
      await this.regionScope.assertLampRowWritableByUser(user, existing);
    }

    let regionId: string | undefined =
      typeof dto.regionId === 'string' && dto.regionId.trim() ? dto.regionId.trim() : undefined;
    if (!regionId && typeof dto.regionCode === 'string' && dto.regionCode.trim()) {
      const region = await this.prisma.region.findUnique({ where: { code: dto.regionCode.trim() } });
      regionId = region?.id;
    }

    const lng = dto.longitude !== undefined && dto.longitude !== null ? Number(dto.longitude) : undefined;
    const lat = dto.latitude !== undefined && dto.latitude !== null ? Number(dto.latitude) : undefined;
    if (!regionId && lng !== undefined && lat !== undefined && Number.isFinite(lng) && Number.isFinite(lat)) {
      regionId = (await this.regionScope.findNearestRegionId(lng, lat)) ?? undefined;
    }

    if (!existing && !regionId) {
      throw new BadRequestException('新建路灯时需提供 regionId、regionCode 或有效经纬度以匹配区域');
    }

    if (!existing && (lng === undefined || lat === undefined || !Number.isFinite(lng) || !Number.isFinite(lat))) {
      throw new BadRequestException('新建路灯时必须提供有效的 longitude 与 latitude');
    }

    const name =
      typeof dto.name === 'string' && dto.name.trim()
        ? dto.name.trim()
        : existing?.name ?? code;

    const address =
      dto.address === undefined ? undefined : dto.address === null ? null : String(dto.address);
    const deviceType = this.normalizeDeviceType(dto.deviceType);

    const status = this.parseLampStatus(dto.status, existing?.status);
    const onlineStatus = this.parseOnlineStatus(dto.onlineStatus, existing?.onlineStatus);

    const isPowerOn =
      dto.isPowerOn === undefined ? undefined : dto.isPowerOn === null ? null : Boolean(dto.isPowerOn);

    let lastReportedAt: Date | null | undefined;
    if (dto.lastReportedAt === null) {
      lastReportedAt = null;
    } else if (typeof dto.lastReportedAt === 'string' && dto.lastReportedAt) {
      const d = new Date(dto.lastReportedAt);
      lastReportedAt = Number.isNaN(d.getTime()) ? undefined : d;
    }

    let tenantIdFromBody: string | null | undefined;
    if (user.tenantId == null) {
      if (dto.tenantId === null) {
        tenantIdFromBody = null;
      } else if (typeof dto.tenantId === 'string' && dto.tenantId.trim()) {
        tenantIdFromBody = dto.tenantId.trim();
      }
    }

    const effectiveTenantId = user.tenantId != null ? user.tenantId : tenantIdFromBody;

    if (existing) {
      if (regionId) {
        await this.regionScope.assertRegionWritableByTenantUser(user, regionId);
      }
      const data: Prisma.StreetLampUpdateInput = {
        name,
        ...(address !== undefined ? { address } : {}),
        ...(deviceType !== undefined ? { deviceType } : {}),
        ...(lng !== undefined && Number.isFinite(lng) ? { longitude: lng } : {}),
        ...(lat !== undefined && Number.isFinite(lat) ? { latitude: lat } : {}),
        ...(status !== undefined ? { status } : {}),
        ...(onlineStatus !== undefined ? { onlineStatus } : {}),
        ...(isPowerOn !== undefined ? { isPowerOn } : {}),
        ...(lastReportedAt !== undefined ? { lastReportedAt } : {}),
        ...(regionId ? { region: { connect: { id: regionId } } } : {}),
      };
      if (user.tenantId == null && tenantIdFromBody !== undefined) {
        data.tenant = tenantIdFromBody ? { connect: { id: tenantIdFromBody } } : { disconnect: true };
      }
      return this.prisma.streetLamp.update({
        where: { id: existing.id },
        data,
        include: { region: true },
      });
    }

    await this.regionScope.assertRegionWritableByTenantUser(user, regionId!);

    return this.prisma.streetLamp.create({
      data: {
        code,
        name,
        tenantId: effectiveTenantId ?? null,
        regionId: regionId!,
        address: address ?? null,
        longitude: lng!,
        latitude: lat!,
        deviceType: deviceType ?? '单灯',
        status: status ?? 'NORMAL',
        onlineStatus: onlineStatus ?? 'ONLINE',
        isPowerOn: isPowerOn ?? null,
        lastReportedAt: lastReportedAt ?? undefined,
      },
      include: { region: true },
    });
  }

  private parseLampStatus(value: unknown, fallback?: LampStatus): LampStatus | undefined {
    if (value === undefined || value === null || value === '') {
      return fallback;
    }
    const v = String(value).toUpperCase() as LampStatus;
    if (!LAMP_STATUSES.includes(v)) {
      throw new BadRequestException(`status 枚举值不合法：${String(value)}`);
    }
    return v;
  }

  private normalizeDeviceType(value: unknown) {
    if (value === undefined) {
      return undefined;
    }
    return value === '监控终端' ? '监控终端' : '单灯';
  }

  private parseOnlineStatus(value: unknown, fallback?: OnlineStatus): OnlineStatus | undefined {
    if (value === undefined || value === null || value === '') {
      return fallback;
    }
    const v = String(value).toUpperCase() as OnlineStatus;
    if (!ONLINE_STATUSES.includes(v)) {
      throw new BadRequestException(`onlineStatus 枚举值不合法：${String(value)}`);
    }
    return v;
  }
}
