import { Injectable } from '@nestjs/common';
import { AuthUser } from '../../common/current-user.decorator';
import { RegionScopeService } from '../../common/region-scope.service';
import { PrismaService } from '../prisma/prisma.service';

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

  async summary(user: AuthUser, query: any = {}) {
    const recentPage = Math.max(Number(query.recentPage ?? 1), 1);
    const recentPageSize = Math.min(Math.max(Number(query.recentPageSize ?? 6), 1), 100);
    const now = new Date();
    const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const trendStart = new Date(now.getFullYear(), now.getMonth() - 11, 1);
    const lampWhere = await this.regionScope.lampWhere(user, {}, query.regionId);
    const alarmWhere = await this.regionScope.alarmWhere(user, {}, query.regionId);
    const singleLampWhere = { AND: [lampWhere, { deviceType: '单灯' }] };
    const terminalWhere = { AND: [lampWhere, { deviceType: '监控终端' }] };
    const [
      assetTotal,
      singleLampTotal,
      terminalTotal,
      onlineTotal,
      singleLampOnlineTotal,
      terminalOnlineTotal,
      openAlarms,
      criticalAlarms,
      alarmsByStatus,
      alarmsByLevel,
      lampsByStatus,
      terminalsByStatus,
      regionRanking,
      regionDeviceRanking,
      regionOnlineRanking,
      regionOpenAlarmRanking,
      deviceTypes,
      todayAlarms,
      lampCreatedRows,
      recentAlarms,
      recentAlarmsTotal,
    ] = await Promise.all([
      this.prisma.streetLamp.count({ where: lampWhere }),
      this.prisma.streetLamp.count({ where: singleLampWhere }),
      this.prisma.streetLamp.count({ where: terminalWhere }),
      this.prisma.streetLamp.count({ where: { ...lampWhere, onlineStatus: 'ONLINE' } }),
      this.prisma.streetLamp.count({ where: { AND: [singleLampWhere, { onlineStatus: 'ONLINE' }] } }),
      this.prisma.streetLamp.count({ where: { AND: [terminalWhere, { onlineStatus: 'ONLINE' }] } }),
      this.prisma.alarm.count({ where: { ...alarmWhere, status: { in: ['OPEN', 'PROCESSING'] } } }),
      this.prisma.alarm.count({ where: { ...alarmWhere, level: 'CRITICAL', status: { in: ['OPEN', 'PROCESSING'] } } }),
      this.prisma.alarm.groupBy({ by: ['status'], where: alarmWhere, _count: true }),
      this.prisma.alarm.groupBy({ by: ['level'], where: alarmWhere, _count: true }),
      this.prisma.streetLamp.groupBy({ by: ['status'], where: singleLampWhere, _count: true }),
      this.prisma.streetLamp.groupBy({ by: ['status'], where: terminalWhere, _count: true }),
      this.prisma.alarm.groupBy({
        by: ['regionId'],
        where: alarmWhere,
        _count: true,
        orderBy: { _count: { regionId: 'desc' } },
        take: 8,
      }),
      this.prisma.streetLamp.groupBy({
        by: ['regionId'],
        where: lampWhere,
        _count: true,
        orderBy: { _count: { regionId: 'desc' } },
        take: 8,
      }),
      this.prisma.streetLamp.groupBy({
        by: ['regionId'],
        where: { ...lampWhere, onlineStatus: 'ONLINE' },
        _count: true,
      }),
      this.prisma.alarm.groupBy({
        by: ['regionId'],
        where: { ...alarmWhere, status: { in: ['OPEN', 'PROCESSING'] } },
        _count: true,
      }),
      this.prisma.streetLamp.groupBy({
        by: ['deviceType'],
        where: lampWhere,
        _count: true,
        orderBy: { _count: { deviceType: 'desc' } },
      }),
      this.prisma.alarm.count({ where: { ...alarmWhere, occurredAt: { gte: todayStart } } }),
      this.prisma.streetLamp.findMany({
        where: lampWhere,
        select: { regionId: true, createdAt: true },
      }),
      this.prisma.alarm.findMany({
        where: alarmWhere,
        include: { lamp: true, region: true },
        orderBy: { occurredAt: 'desc' },
        skip: (recentPage - 1) * recentPageSize,
        take: recentPageSize,
      }),
      this.prisma.alarm.count({ where: alarmWhere }),
    ]);

    const countOf = (item: any) =>
      typeof item?._count === 'number' ? item._count : Number(item?._count?._all ?? Object.values(item?._count ?? {})[0] ?? 0);
    const alarmCountByRegion = new Map(regionOpenAlarmRanking.map((item) => [item.regionId, countOf(item)]));
    const onlineCountByRegion = new Map(regionOnlineRanking.map((item) => [item.regionId, countOf(item)]));
    const rankedRegionIds = [...new Set([
      ...regionRanking.map((item) => item.regionId),
      ...regionDeviceRanking.map((item) => item.regionId),
      ...lampCreatedRows.map((item) => item.regionId),
    ])];
    const regions = await this.prisma.region.findMany({
      where: rankedRegionIds.length ? { id: { in: rankedRegionIds } } : undefined,
      select: { id: true, name: true, level: true, parentId: true, longitude: true, latitude: true },
    });
    const allRegions = await this.prisma.region.findMany({
      select: { id: true, name: true, level: true, parentId: true, longitude: true, latitude: true },
    });
    const regionLookup = new Map(allRegions.map((region) => [region.id, region]));
    const displayRegion = (regionId: string) => regions.find((region) => region.id === regionId) ?? regionLookup.get(regionId);
    const cityRegionId = (regionId: string) => {
      let region = regionLookup.get(regionId);
      let fallback = region;
      while (region?.parentId) {
        if (region.level === 2) {
          return region.id;
        }
        fallback = region;
        region = regionLookup.get(region.parentId);
      }
      return region?.level === 2 ? region.id : fallback?.id ?? regionId;
    };
    const accessCityCount = new Set(lampCreatedRows.map((item) => cityRegionId(item.regionId))).size;
    const accessRegionCount = new Set(lampCreatedRows.map((item) => item.regionId)).size;
    const yesterdayLampRows = lampCreatedRows.filter((item) => item.createdAt < todayStart);
    const todayLampRows = lampCreatedRows.filter((item) => item.createdAt >= todayStart);
    const yesterdayCityCount = new Set(yesterdayLampRows.map((item) => cityRegionId(item.regionId))).size;
    const yesterdayRegionCount = new Set(yesterdayLampRows.map((item) => item.regionId)).size;
    const accessTrend = this.buildAccessTrend(lampCreatedRows, trendStart, now, cityRegionId);
    const deviceTypeStats = deviceTypes.map((item) => ({
      name: item.deviceType ?? '未分类',
      count: countOf(item),
    }));
    const deviceTypeCount = (keyword: string) =>
      deviceTypeStats
        .filter((item) => item.name.includes(keyword))
        .reduce((sum, item) => sum + item.count, 0);
    const regionDeviceOverview = regionDeviceRanking.map((item) => {
      const region = displayRegion(item.regionId);
      const count = countOf(item);
      const online = onlineCountByRegion.get(item.regionId) ?? 0;
      return {
        regionId: item.regionId,
        regionName: region?.name ?? item.regionId,
        total: count,
        online,
        onlineRate: count ? Number(((online / count) * 100).toFixed(2)) : 0,
        alarms: alarmCountByRegion.get(item.regionId) ?? 0,
        longitude: region?.longitude === null || region?.longitude === undefined ? null : Number(region.longitude),
        latitude: region?.latitude === null || region?.latitude === undefined ? null : Number(region.latitude),
      };
    });

    return {
      assetTotal,
      accessCityCount,
      accessRegionCount,
      metricChanges: {
        accessCityCount: accessCityCount - yesterdayCityCount,
        accessRegionCount: accessRegionCount - yesterdayRegionCount,
        assetTotal: todayLampRows.length,
      },
      projectTotal: null,
      hasProjectTable: false,
      hasEnergyTable: false,
      lampTotal: singleLampTotal,
      terminalTotal,
      onlineTotal,
      lampOnlineTotal: singleLampOnlineTotal,
      terminalOnlineTotal,
      onlineRate: assetTotal ? Number(((onlineTotal / assetTotal) * 100).toFixed(2)) : 0,
      lampOnlineRate: singleLampTotal ? Number(((singleLampOnlineTotal / singleLampTotal) * 100).toFixed(2)) : 0,
      terminalOnlineRate: terminalTotal ? Number(((terminalOnlineTotal / terminalTotal) * 100).toFixed(2)) : 0,
      openAlarms,
      todayAlarms,
      criticalAlarms,
      alarmsByStatus,
      alarmsByLevel,
      lampsByStatus,
      terminalsByStatus,
      deviceTypeStats,
      deviceBaseStats: {
        lamp: singleLampTotal,
        terminal: terminalTotal,
        distributionBox: deviceTypeCount('配电箱'),
        camera: deviceTypeCount('摄像'),
        sensor: deviceTypeCount('传感'),
        energyCollector: deviceTypeCount('能耗'),
      },
      accessTrend,
      regionDeviceRanking: regionDeviceOverview,
      mapRegions: regionDeviceOverview.filter((item) => item.longitude !== null && item.latitude !== null),
      energy: null,
      recentAlarms: recentAlarms.map((alarm) => ({
        id: alarm.id,
        title: alarm.title,
        type: alarm.type,
        level: alarm.level,
        status: alarm.status,
        occurredAt: alarm.occurredAt,
        lampName: alarm.lamp.name,
        lampCode: alarm.lamp.code,
        deviceType: alarm.lamp.deviceType,
        regionName: alarm.region.name,
      })),
      recentAlarmsTotal,
      recentAlarmsPage: recentPage,
      recentAlarmsPageSize: recentPageSize,
      regionRanking: regionRanking.map((item) => ({
        regionId: item.regionId,
        regionName: displayRegion(item.regionId)?.name ?? item.regionId,
        count: countOf(item),
      })),
    };
  }

  private buildAccessTrend(
    lampRows: { regionId: string; createdAt: Date }[],
    trendStart: Date,
    now: Date,
    cityRegionId: (regionId: string) => string,
  ) {
    return Array.from({ length: 12 }, (_, index) => {
      const monthStart = new Date(trendStart.getFullYear(), trendStart.getMonth() + index, 1);
      const monthEnd = new Date(monthStart.getFullYear(), monthStart.getMonth() + 1, 1);
      const rows = lampRows.filter((row) => row.createdAt < monthEnd && row.createdAt <= now);
      return {
        month: `${monthStart.getFullYear()}-${String(monthStart.getMonth() + 1).padStart(2, '0')}`,
        accessCityCount: new Set(rows.map((row) => cityRegionId(row.regionId))).size,
        deviceCount: rows.length,
      };
    });
  }
}
