import { Provide, Inject } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { ReportDailyVehicleEntity } from '../entity/dailyvehicle';
import { PushVehicleGpsEntity } from '../../push/entity/vehiclegps';
import { BaseSysParamService } from '../../base/service/sys/param';



@Provide()
export class TaskServiceVehicle extends BaseService {
    @InjectEntityModel(ReportDailyVehicleEntity)
    reportDailyVehicleEntity: Repository<ReportDailyVehicleEntity>;

    @InjectEntityModel(PushVehicleGpsEntity)
    pushVehicleGpsEntity: Repository<PushVehicleGpsEntity>;

    @Inject()
    baseSysParamService: BaseSysParamService;

    async aggregateDailyData(targetDate: string) {
        const startTime = `${targetDate} 00:00:00`;
        const endTime = `${targetDate} 23:59:59`;

        let HOUR_STARTWORK = parseInt(await this.baseSysParamService.dataByKey('vehicle.hour.startwork')); //'6';
        let HOUR_OFFWORK = parseInt(await this.baseSysParamService.dataByKey('vehicle.hour.offwork')); //'22';
        let THRESHOLD_MOVING = parseInt(await this.baseSysParamService.dataByKey('vehicle.speed.threshold.moving')); //'2.0 km/h';
        let THRESHOLD_SPEEDING = parseInt(await this.baseSysParamService.dataByKey('vehicle.speed.threshold.speeding')); //'30 km/h';

        HOUR_STARTWORK = Number.isNaN(HOUR_STARTWORK) ? 6 : HOUR_STARTWORK;
        HOUR_OFFWORK = Number.isNaN(HOUR_OFFWORK) ? 22 : HOUR_OFFWORK;
        THRESHOLD_MOVING = Number.isNaN(THRESHOLD_MOVING) ? 2 : THRESHOLD_MOVING;
        THRESHOLD_SPEEDING = Number.isNaN(THRESHOLD_SPEEDING) ? 30 : THRESHOLD_SPEEDING;



        const rawGpsList = await this.pushVehicleGpsEntity.createQueryBuilder('gps')
            .select([
                'gps.vehicleNo AS vehicleNo',             // 拿最新的自增ID作为每日表的 siteId
                'gps.departmentId AS departmentId', // 永远拿当前后台最新的站点名称，防止设备传旧名称
                'gps.gpsTime AS gpsTime',
                'gps.longitude AS longitude',
                'gps.latitude AS latitude',
                'gps.speed AS speed',
                'gps.direction AS direction',
                'gps.elevation AS elevation'
            ])
            .where('gps.gpsTime BETWEEN :start AND :end', { start: startTime, end: endTime })
            .orderBy({
                'gps.vehicleNo': 'ASC',
                'gps.gpsTime': 'ASC',
            })
            .getRawMany();

        if (!rawGpsList || rawGpsList.length === 0) return;

        // 2. 按车牌号 vehicleNo 分组
        const groupedMap = new Map<string, PushVehicleGpsEntity[]>();
        for (const point of rawGpsList) {
            if (!groupedMap.has(point.vehicleNo)) {
                groupedMap.set(point.vehicleNo, []);
            }
            groupedMap.get(point.vehicleNo)?.push(point);
        }

        // 3. 计算截至当前（或全天）经历的总分钟数，用于倒推计算 offlineMinutes
        const startMs = new Date(`${targetDate}T00:00:00`).getTime();
        const endMs = new Date(`${targetDate}T23:59:59`).getTime();
        const nowMs = new Date().getTime();

        // 如果 targetDate 是今天，计算到当前时间；如果是过去/未来，上限算到 23:59:59
        const effectiveEndMs = nowMs < endMs ? nowMs : endMs;
        const totalElapsedMinutes = Math.max(1, Math.floor((effectiveEndMs - startMs) / (1000 * 60)));

        const summariesToUpsert: Partial<ReportDailyVehicleEntity>[] = [];

        // 4. 逐车按严格顺序计算各项指标
        for (const [vehicleNo, points] of groupedMap.entries()) {
            const summary = this.calculateVehicleMetrics(
                targetDate,
                vehicleNo,
                points,
                totalElapsedMinutes,
                HOUR_STARTWORK,
                HOUR_OFFWORK,
                THRESHOLD_MOVING,
                THRESHOLD_SPEEDING
            );
            summariesToUpsert.push(summary);
        }

        // 5. 执行 UPSERT 覆盖更新
        await this.upsertDailyReports(summariesToUpsert);


        return summariesToUpsert.length;
    }


    /**
       * 核心单车计算引擎（严格按照指标依赖逻辑顺序执行）
       */
    private calculateVehicleMetrics(
        targetDate: string,
        vehicleNo: string,
        points: PushVehicleGpsEntity[],
        totalElapsedMinutes: number,
        HOUR_STARTWORK: number,
        HOUR_OFFWORK: number,
        HHRESHOLD_MOVING: number,
        THRESHOLD_SPEEDING: number
    ): Partial<ReportDailyVehicleEntity> {
        const departmentId = points[0]?.departmentId ?? null;

        // 基础统计量
        let totalMeter = 0; // 累计行驶距离(米)
        let secondsMoving = 0; // 纯行驶时长(秒)
        let secondsParked = 0; // 静止停靠时长(秒)
        let secondsAbnormalIdle = 0; // 异常怠速时长(秒)

        // 安全驾驶统计量
        let overspeedCount = 0; // 超速次数
        let overspeedDurationSec = 0; // 超速总秒数
        let harshBrakeCount = 0; // 急刹车次数
        let harshAccelCount = 0; // 急加速次数
        let harshTurnCount = 0; // 急转弯次数
        let offHoursMoveCount = 0; // 非工作时段异动次数

        let isOverspeeding = false; // 超速状态标记

        for (let i = 0; i < points.length; i++) {
            const curr = points[i];
            const speed = Number(curr.speed) || 0;
            const currDate = new Date(curr.gpsTime);

            // ====================================================
            // 顺序 1：单点状态判断（只依赖当前点的数据）
            // ====================================================

            // A. 判断非工作时段异动 (23:00 - 05:00 且速度 > 2 km/h)
            const hour = currDate.getHours();
            if ((hour >= HOUR_OFFWORK || hour < HOUR_STARTWORK) && speed > HHRESHOLD_MOVING) {
                offHoursMoveCount++;
            }

            // B. 判断超速告警 (限速 30 km/h)
            if (speed > THRESHOLD_SPEEDING) {
                overspeedDurationSec += 1; // 按 1 秒/采样点粗略累加
                if (!isOverspeeding) {
                    overspeedCount++; // 刚跨过 20 时记录一次新告警
                    isOverspeeding = true;
                }
            } else {
                isOverspeeding = false;
            }

            // ====================================================
            // 顺序 2：相邻点微分与物理量计算（必须依赖前一点 i-1）
            // ====================================================
            if (i > 0) {
                const prev = points[i - 1];
                const prevDate = new Date(prev.gpsTime);

                // 计算相邻两点时间差（秒）
                const timeDiffSec = Math.floor((currDate.getTime() - prevDate.getTime()) / 1000);

                // 过滤网络断连/关机（时间差 > 120 秒 视为掉线，不参与物理微分计算）
                if (timeDiffSec > 0 && timeDiffSec <= 120) {

                    // ================================================
                    // 顺序 3：基于速度划分为【行驶】或【静止停靠】
                    // ================================================
                    if (speed > HHRESHOLD_MOVING) {
                        // 3.1 累加纯行驶时长
                        secondsMoving += timeDiffSec;

                        // 3.2 累加球面真实位移距离
                        totalMeter += this.getHaversineDistance(
                            Number(prev.latitude),
                            Number(prev.longitude),
                            Number(curr.latitude),
                            Number(curr.longitude),
                        );
                    } else {
                        // 3.3 累加静止停靠时长
                        secondsParked += timeDiffSec;

                        // 3.4 静止停靠超过 15 分钟（900秒）算作异常怠速
                        if (timeDiffSec > 900) {
                            secondsAbnormalIdle += timeDiffSec;
                        }
                    }

                    // ================================================
                    // 顺序 4：安全驾驶行为（加速度与转弯角微分计算）
                    // ================================================

                    // 4.1 加速度计算: a = (v2 - v1) / t (需要转换为 m/s)
                    const v1 = (Number(prev.speed) || 0) / 3.6;
                    const v2 = speed / 3.6;
                    const accel = (v2 - v1) / timeDiffSec;

                    if (accel > 3.0) {
                        harshAccelCount++; // 急加速 a > 3.0 m/s²
                    } else if (accel < -3.5) {
                        harshBrakeCount++; // 急刹车 a < -3.5 m/s²
                    }

                    // 4.2 急转弯计算: 2秒内航向角差 > 60° 且车速 > 15 km/h
                    const prevDir = Number(prev.direction) || 0;
                    const currDir = Number(curr.direction) || 0;
                    let dirDiff = Math.abs(currDir - prevDir);
                    if (dirDiff > 180) dirDiff = 360 - dirDiff; // 跨 0/360 度修正

                    if (timeDiffSec <= 2 && dirDiff > 60 && speed > 15) {
                        harshTurnCount++;
                    }
                }
            }
        }

        // ====================================================
        // 顺序 5：衍生指标倒推计算（必须放在最后）
        // ====================================================

        // 5.1 秒转化为分钟
        const minutesMoving = Math.round(secondsMoving / 60);
        const minutesParked = Math.round(secondsParked / 60);
        const minutesAbnormalIdle = Math.round(secondsAbnormalIdle / 60);
        const totalKm = Number((totalMeter / 1000).toFixed(2));

        // 5.2 倒推【未启动/离线时长】 = 当天已过总时长 - 行驶时长 - 停靠时长
        const minutesOffline = Math.max(
            0,
            totalElapsedMinutes - minutesMoving - minutesParked,
        );

        // 5.3 计算【平均行驶车速】 = 总行驶公里数 / 纯行驶小时数
        const hoursMoving = secondsMoving / 3600;
        const avgSpeed =
            hoursMoving > 0 ? Number((totalKm / hoursMoving).toFixed(1)) : 0.0;

        return {
            date: targetDate,
            vehicleNo,
            departmentId,
            totalKm,
            minutesMoving,
            minutesParked,
            minutesOffline,
            minutesAbnormalIdle,
            avgSpeed,
            overspeedCount,
            overspeedDurationSec,
            harshBrakeCount,
            harshAccelCount,
            harshTurnCount,
            offHoursMoveCount,
        };
    }

    /**
     * 数据库批量覆盖更新 (ON DUPLICATE KEY UPDATE)
     */
    private async upsertDailyReports(finalRecords: Partial<ReportDailyVehicleEntity>[]) {
        if (finalRecords.length === 0) return;

        // 1. 动态抓取所有的键，将主键、联合唯一键彻底从“待更新字段”中排除
        // 这样能确保 ON DUPLICATE KEY UPDATE 后面的赋值语句绝对干净
        const allUpdateFields = Object.keys(finalRecords[0]).filter(
            k => k !== 'date' && k !== 'vehicleNo' && k !== 'id'
        );

        // 2. 依然推荐采用 Chunk 分批，这是应对海量数据/多子字段最安全、执行效率最高的做法
        const chunkSize = 50;
        for (let i = 0; i < finalRecords.length; i += chunkSize) {
            const chunk = finalRecords.slice(i, i + chunkSize);

            await this.reportDailyVehicleEntity.createQueryBuilder()
                .insert()
                .values(chunk)
                // 💡 显式声明：当 ['date', 'siteId'] 发生冲突时，强行把 allUpdateFields 里的字段全部更新一遍
                .orUpdate(allUpdateFields, ['date', 'vehicleNo'])
                .execute();
        }

    }

    /**
     * 纯 JS 原生计算两点之间的球面距离 (单位: 米)
     */
    private getHaversineDistance(
        lat1: number,
        lon1: number,
        lat2: number,
        lon2: number,
    ): number {
        if (lat1 === lat2 && lon1 === lon2) return 0;
        const R = 6371000;
        const rad = Math.PI / 180;
        const dLat = (lat2 - lat1) * rad;
        const dLon = (lon2 - lon1) * rad;
        const a =
            Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(lat1 * rad) *
            Math.cos(lat2 * rad) *
            Math.sin(dLon / 2) *
            Math.sin(dLon / 2);
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c;
    }
}
