import { Provide } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';

import { ReportDailySorterEntity } from '../entity/dailysorter';

import { BasicdataSorterEntity } from '../../basicdata/entity/sorter';

import { PushSorterAttendanceEntity } from '../../push/entity/sorterattendance';
import { PushSorterGpsEntity } from '../../push/entity/sortergps';
import { PushSorterHealthEntity } from '../../push/entity/sorterhealth';
import { PushSorterAlarmEntity } from '../../push/entity/sorteralarm';

import { RecycleOrderEntity } from '../../shop/entity/recycleorder';


/**
 * 分拣员日报统计
 *
 * 统计原则：
 *
 * 1. BasicdataSorterEntity 是分拣员主数据
 * 2. businessId 是分拣员业务唯一标识
 * 3. 一名分拣员一天一条日报
 * 4. date + businessId 唯一
 *
 * 数据来源：
 *
 * BasicdataSorter
 *      ├── Attendance
 *      ├── GPS
 *      ├── Health
 *      ├── Alarm
 *      └── RecycleOrder
 *
 *              ↓
 *
 *       ReportDailySorter
 */
@Provide()
export class TaskServiceSorter extends BaseService {

  /**
   * 分拣员基础信息
   */
  @InjectEntityModel(BasicdataSorterEntity)
  basicdataSorterEntity: Repository<BasicdataSorterEntity>;

  /**
   * 分拣员日报
   */
  @InjectEntityModel(ReportDailySorterEntity)
  reportDailySorterEntity: Repository<ReportDailySorterEntity>;

  /**
   * 分拣员考勤
   */
  @InjectEntityModel(PushSorterAttendanceEntity)
  pushSorterAttendanceEntity: Repository<PushSorterAttendanceEntity>;

  /**
   * 分拣员GPS
   */
  @InjectEntityModel(PushSorterGpsEntity)
  pushSorterGpsEntity: Repository<PushSorterGpsEntity>;

  /**
   * 分拣员健康
   */
  @InjectEntityModel(PushSorterHealthEntity)
  pushSorterHealthEntity: Repository<PushSorterHealthEntity>;

  /**
   * 分拣员健康告警
   */
  @InjectEntityModel(PushSorterAlarmEntity)
  pushSorterAlarmEntity: Repository<PushSorterAlarmEntity>;

  /**
   * 回收订单
   */
  @InjectEntityModel(RecycleOrderEntity)
  recycleOrderEntity: Repository<RecycleOrderEntity>;


  /**
   * =========================================================
   * 每日统计
   * =========================================================
   *
   * @param targetDate 统计日期，例如：2026-08-16
   */
  async aggregateDailyData(targetDate: string) {

    const startTime = `${targetDate} 00:00:00`;
    const endTime = `${targetDate} 23:59:59`;

    // =======================================================
    // 1. 查询当天有效的分拣员
    // =======================================================

    const sorterList =
      await this.getActiveSorters(targetDate);

    if (!sorterList.length) {
      return 0;
    }


    // =======================================================
    // 2. 初始化日报
    //
    // 注意：
    // 这里以分拣员基础信息为准，而不是以当天产生的数据为准。
    //
    // 即使某个分拣员当天没有：
    // GPS / 健康 / 订单 / 考勤
    //
    // 也会产生一条日报。
    // =======================================================

    const summaryMap =
      new Map<string, Partial<ReportDailySorterEntity>>();

    for (const sorter of sorterList) {

      summaryMap.set(
        sorter.businessId,
        {
          date: targetDate,

          businessId: sorter.businessId,
          businessName: sorter.businessName,

          departmentId: sorter.departmentId,
          workSiteId: sorter.workSiteId,

          checkInTime: null,
          checkOutTime: null,
          workHours: 0,
          attendanceStatus: '缺卡',

          gpsPointCount: 0,
          totalKm: 0,
          minutesMoving: 0,

          orderCount: 0,
          completedOrderCount: 0,
          appointmentOrderCount: 0,
          totalCarbonPoint: 0,

          healthDataCount: 0,
          healthAbnormalCount: 0,

          alarmCount: 0,
          generalAlarmCount: 0,
          mediumAlarmCount: 0,
          seriousAlarmCount: 0,
          unhandledAlarmCount: 0,
        }
      );
    }


    // =======================================================
    // 3. 考勤统计
    // =======================================================

    await this.aggregateAttendance(
      targetDate,
      summaryMap
    );


    // =======================================================
    // 4. GPS统计
    // =======================================================

    await this.aggregateGps(
      startTime,
      endTime,
      summaryMap
    );


    // =======================================================
    // 5. 健康数据统计
    // =======================================================

    await this.aggregateHealth(
      startTime,
      endTime,
      summaryMap
    );


    // =======================================================
    // 6. 健康告警统计
    // =======================================================

    await this.aggregateAlarm(
      startTime,
      endTime,
      summaryMap
    );


    // =======================================================
    // 7. 回收订单统计
    // =======================================================

    await this.aggregateRecycleOrder(
      startTime,
      endTime,
      sorterList,
      summaryMap
    );


    // =======================================================
    // 8. 保存日报
    // =======================================================

    const records =
      Array.from(summaryMap.values());

    await this.upsertDailyReports(records);

    return records.length;
  }


  /**
   * =========================================================
   * 查询当天有效分拣员
   * =========================================================
   *
   * 状态：
   *
   * 0 = 在职
   * 1 = 离职
   * 2 = 禁用
   *
   * 同时考虑：
   *
   * hireDate
   * leaveDate
   */
  private async getActiveSorters(
    targetDate: string,
  ): Promise<BasicdataSorterEntity[]> {

    return await this.basicdataSorterEntity
      .createQueryBuilder('s')

      .where('s.status = :status', {
        status: 0,
      })

      .andWhere(
        '(s.hireDate IS NULL OR s.hireDate <= :targetDate)',
        {
          targetDate,
        }
      )

      .andWhere(
        '(s.leaveDate IS NULL OR s.leaveDate >= :targetDate)',
        {
          targetDate,
        }
      )

      .getMany();
  }


  /**
   * =========================================================
   * 考勤统计
   * =========================================================
   */
  private async aggregateAttendance(
    targetDate: string,
    summaryMap: Map<
      string,
      Partial<ReportDailySorterEntity>
    >,
  ) {

    const list =
      await this.pushSorterAttendanceEntity
        .createQueryBuilder('a')
        .where(
          'a.workDate = :targetDate',
          {
            targetDate,
          }
        )
        .getMany();


    for (const item of list) {

      const summary =
        summaryMap.get(item.idCard);

      if (!summary) {
        continue;
      }


      summary.checkInTime =
        item.checkInTime || null;

      summary.checkOutTime =
        item.checkOutTime || null;

      summary.workHours =
        Number(item.workHours || 0);

      summary.attendanceStatus =
        item.status || '缺卡';
    }
  }


  /**
   * =========================================================
   * GPS统计
   * =========================================================
   *
   * 统计：
   *
   * gpsPointCount
   * totalKm
   * minutesMoving
   *
   * GPS默认按照上传时间升序排列。
   */
  private async aggregateGps(
    startTime: string,
    endTime: string,
    summaryMap: Map<
      string,
      Partial<ReportDailySorterEntity>
    >,
  ) {

    const list =
      await this.pushSorterGpsEntity
        .createQueryBuilder('gps')

        .select([
          'gps.idCard AS idCard',
          'gps.uploadTime AS uploadTime',
          'gps.longitude AS longitude',
          'gps.latitude AS latitude',
          'gps.speed AS speed',
        ])

        .where(
          'gps.uploadTime BETWEEN :startTime AND :endTime',
          {
            startTime,
            endTime,
          }
        )

        .orderBy('gps.idCard', 'ASC')
        .addOrderBy('gps.uploadTime', 'ASC')

        .getRawMany();


    // -------------------------------------------------------
    // 按人员分组
    // -------------------------------------------------------

    const gpsMap =
      new Map<string, any[]>();


    for (const item of list) {

      if (!item.idCard) {
        continue;
      }

      // 只处理当天有效分拣员
      if (!summaryMap.has(item.idCard)) {
        continue;
      }


      if (!gpsMap.has(item.idCard)) {

        gpsMap.set(
          item.idCard,
          []
        );
      }


      gpsMap
        .get(item.idCard)!
        .push(item);
    }


    // -------------------------------------------------------
    // 计算每个人的轨迹
    // -------------------------------------------------------

    for (const [
      businessId,
      points,
    ] of gpsMap.entries()) {

      const summary =
        summaryMap.get(businessId);

      if (!summary) {
        continue;
      }


      summary.gpsPointCount =
        points.length;


      let totalMeter = 0;

      let movingSeconds = 0;


      for (
        let i = 1;
        i < points.length;
        i++
      ) {

        const prev =
          points[i - 1];

        const curr =
          points[i];


        const prevTime =
          new Date(
            prev.uploadTime
          ).getTime();

        const currTime =
          new Date(
            curr.uploadTime
          ).getTime();


        const diffSeconds =
          (currTime - prevTime) / 1000;


        // ---------------------------------------------------
        // 异常GPS时间间隔
        //
        // 正常采样约5分钟
        // 超过10分钟认为这两个点不应该直接连线
        // ---------------------------------------------------

        if (
          diffSeconds <= 0 ||
          diffSeconds > 600
        ) {
          continue;
        }


        const speed =
          Number(curr.speed || 0);


        // ---------------------------------------------------
        // speed > 2 km/h
        // 认为分拣员正在移动
        // ---------------------------------------------------

        if (speed > 2) {

          movingSeconds +=
            diffSeconds;


          // km/h → m/s → 米
          const meter =
            speed *
            1000 /
            3600 *
            diffSeconds;


          totalMeter +=
            meter;
        }
      }


      summary.totalKm =
        Number(
          (
            totalMeter / 1000
          ).toFixed(2)
        );


      summary.minutesMoving =
        Math.round(
          movingSeconds / 60
        );
    }
  }


  /**
   * =========================================================
   * 健康数据统计
   * =========================================================
   *
   * 不在日报保存：
   *
   * 体温
   * 血压
   * 血氧
   * 心率
   *
   * 日报只统计：
   *
   * 健康数据数量
   * 健康异常数量
   */
  private async aggregateHealth(
    startTime: string,
    endTime: string,
    summaryMap: Map<
      string,
      Partial<ReportDailySorterEntity>
    >,
  ) {

    const list =
      await this.pushSorterHealthEntity
        .createQueryBuilder('h')

        .select([
          'h.idCard AS idCard',
          'h.dataType AS dataType',
        ])

        .where(
          'h.uploadTime BETWEEN :startTime AND :endTime',
          {
            startTime,
            endTime,
          }
        )

        .getRawMany();


    for (const item of list) {

      const summary =
        summaryMap.get(item.idCard);

      if (!summary) {
        continue;
      }


      summary.healthDataCount =
        Number(
          summary.healthDataCount || 0
        ) + 1;


      if (
        item.dataType === '异常'
      ) {

        summary.healthAbnormalCount =
          Number(
            summary.healthAbnormalCount || 0
          ) + 1;
      }
    }
  }


  /**
   * =========================================================
   * 健康告警统计
   * =========================================================
   */
  private async aggregateAlarm(
    startTime: string,
    endTime: string,
    summaryMap: Map<
      string,
      Partial<ReportDailySorterEntity>
    >,
  ) {

    const list =
      await this.pushSorterAlarmEntity
        .createQueryBuilder('a')

        .select([
          'a.idCard AS idCard',
          'a.alarmLevel AS alarmLevel',
          'a.status AS status',
        ])

        .where(
          'a.uploadTime BETWEEN :startTime AND :endTime',
          {
            startTime,
            endTime,
          }
        )

        .getRawMany();


    for (const item of list) {

      const summary =
        summaryMap.get(item.idCard);

      if (!summary) {
        continue;
      }


      // 总告警
      summary.alarmCount =
        Number(
          summary.alarmCount || 0
        ) + 1;


      // ---------------------------------------------------
      // 告警等级
      // ---------------------------------------------------

      switch (item.alarmLevel) {

        case '一般':

          summary.generalAlarmCount =
            Number(
              summary.generalAlarmCount || 0
            ) + 1;

          break;


        case '中等':

          summary.mediumAlarmCount =
            Number(
              summary.mediumAlarmCount || 0
            ) + 1;

          break;


        case '严重':

          summary.seriousAlarmCount =
            Number(
              summary.seriousAlarmCount || 0
            ) + 1;

          break;
      }


      // ---------------------------------------------------
      // status
      //
      // 0 = 未处理
      // 1 = 已处理
      // ---------------------------------------------------

      if (
        Number(item.status) === 0
      ) {

        summary.unhandledAlarmCount =
          Number(
            summary.unhandledAlarmCount || 0
          ) + 1;
      }
    }
  }


  /**
   * =========================================================
   * 回收订单统计
   * =========================================================
   *
   * RecycleOrder：
   *
   * sorterId
   *     ↓
   * BasicdataSorter.id
   *     ↓
   * businessId
   *
   * 统计的是：
   *
   * 当天完成/处理的回收订单
   *
   * 所以使用：
   *
   * finishTime
   */
  private async aggregateRecycleOrder(
    startTime: string,
    endTime: string,

    sorterList: BasicdataSorterEntity[],

    summaryMap: Map<
      string,
      Partial<ReportDailySorterEntity>
    >,
  ) {

    const list =
      await this.recycleOrderEntity
        .createQueryBuilder('o')

        .select([
          'o.sorterId AS sorterId',
          'o.orderType AS orderType',
          'o.status AS status',
          'o.carbonPoint AS carbonPoint',
        ])

        .where(
          'o.finishTime BETWEEN :startTime AND :endTime',
          {
            startTime,
            endTime,
          }
        )

        .andWhere(
          'o.sorterId IS NOT NULL'
        )

        .andWhere(
          'o.sorterId <> 0'
        )

        .getRawMany();


    // -------------------------------------------------------
    // 建立 sorterId → businessId 映射
    // -------------------------------------------------------

    const sorterMap =
      new Map<number, string>();


    for (const sorter of sorterList) {

      sorterMap.set(
        Number(sorter.id),
        sorter.businessId
      );
    }


    // -------------------------------------------------------
    // 统计订单
    // -------------------------------------------------------

    for (const order of list) {

      const sorterId =
        Number(order.sorterId);


      const businessId =
        sorterMap.get(sorterId);


      if (!businessId) {
        continue;
      }


      const summary =
        summaryMap.get(businessId);

      if (!summary) {
        continue;
      }


      // 总订单数
      summary.orderCount =
        Number(
          summary.orderCount || 0
        ) + 1;


      // 完成订单
      if (
        order.status === '完成'
      ) {

        summary.completedOrderCount =
          Number(
            summary.completedOrderCount || 0
          ) + 1;
      }


      // 预约上门订单
      if (
        order.orderType === '预约'
      ) {

        summary.appointmentOrderCount =
          Number(
            summary.appointmentOrderCount || 0
          ) + 1;
      }


      // 发放碳积分
      summary.totalCarbonPoint =
        Number(
          summary.totalCarbonPoint || 0
        ) +
        Number(
          order.carbonPoint || 0
        );
    }
  }


  /**
   * =========================================================
   * 日报 UPSERT
   * =========================================================
   *
   * 唯一键：
   *
   * date + businessId
   *
   * 因此任务重复执行不会产生重复数据。
   */
  private async upsertDailyReports(
    records: Partial<ReportDailySorterEntity>[],
  ) {

    if (!records.length) {
      return;
    }


    await this.reportDailySorterEntity
      .createQueryBuilder()
      .insert()
      .into(ReportDailySorterEntity)
      .values(records)
      .orUpdate(
        [
          'businessName',
          'departmentId',
          'workSiteId',

          'checkInTime',
          'checkOutTime',
          'workHours',
          'attendanceStatus',

          'gpsPointCount',
          'totalKm',
          'minutesMoving',

          'orderCount',
          'completedOrderCount',
          'appointmentOrderCount',
          'totalCarbonPoint',

          'healthDataCount',
          'healthAbnormalCount',

          'alarmCount',
          'generalAlarmCount',
          'mediumAlarmCount',
          'seriousAlarmCount',
          'unhandledAlarmCount',
        ],
        [
          'date',
          'businessId',
        ],
      )
      .execute();
  }

}