import { Injectable } from '@nestjs/common';
import { DingtalkYidaService } from '../sdk/dingtalk-yida.service';
import { DingtalkAttendanceService } from '../sdk/dingtalk-attendance.service';
import { DingtalkAuthService } from '../sdk/dingtalk-auth.service';
import {
  FORM_UUIDS,
  BUSINESS_TRIP_FIELDS,
  BUSINESS_TRIP_CHANGE_FIELDS,
  BUSINESS_TRIP_CHANGE_TYPES,
} from '../constants';
import { SyncLogger } from './sync-logger';
import { getDingtalkSerialNumber, formatTimeDisplay } from '../utils';

export interface SyncExecutionResult {
  success: boolean;
  processedCount: number;
  errors: string[];
  duration: number;
  logs?: string;
  details?: Record<string, any>;
}

@Injectable()
export class BusinessTripSyncService {

  constructor(
    private yidaService: DingtalkYidaService,
    private attendanceService: DingtalkAttendanceService,
    private authService: DingtalkAuthService,
  ) {}

  /**
   * 同步出差数据到钉钉考勤
   * 对应Python: business_trip_to_dingtalk()
   */
  async sync(fromTime?: string, toTime?: string, userId?: string, externalLogger?: SyncLogger): Promise<SyncExecutionResult> {
    const logger = externalLogger || new SyncLogger('BusinessTripSyncService');
    const startTime = Date.now();
    let count = 0;
    const errors: string[] = [];

    logger.log('----开始出差同步----');
    const { displayFrom, displayTo } = formatTimeDisplay(fromTime, toTime);
    logger.log(`处理区间: ${displayFrom} ~ ${displayTo}`);
    if (userId) logger.log(`指定员工: ${userId}`);

    // 收集同步成功的记录，用于后续验证
    const syncedRecords: { userId: string; name: string; fromDate: string; toDate: string; approveId: string }[] = [];

    try {
      const formData = await this.yidaService.searchApprovedInstances(
        FORM_UUIDS.BUSINESS_TRIP,
        fromTime,
        toTime,
        userId,
      );
      logger.log(`从宜搭获取到 ${formData.length} 条审批通过的出差申请${userId ? `（已按员工 ${userId} 过滤）` : ''}`);
      const threeMonthsAgo = Date.now() / 1000 - 60 * 60 * 24 * 90;

      for (const data of formData) {
        const businessTripData = data.formData;
        const creatorId = data.creatorUserId;
        const allTravelTime = businessTripData[BUSINESS_TRIP_FIELDS.TABLE_FIELD] || [];
        const creator = businessTripData[BUSINESS_TRIP_FIELDS.CREATOR];
        const serialNumber = getDingtalkSerialNumber(data);
        const instanceId = data.formInstanceId;

        if (!serialNumber) {
          errors.push(`出差同步缺少流水号: ${creator || creatorId} instanceId=${instanceId}`);
          logger.error(`出差申请缺少 serial number，跳过: ${creator || creatorId} instanceId=${instanceId}`);
          continue;
        }

        let index = 1;
        for (const travelTime of allTravelTime) {
          const approveId = `${serialNumber}-${index}`;

          if (travelTime[BUSINESS_TRIP_FIELDS.TO_DATE] / 1000 < threeMonthsAgo) {
            index++;
            continue;
          }

          const fromDate = this.formatDate(travelTime[BUSINESS_TRIP_FIELDS.FROM_DATE]);
          const fromSlot = travelTime[BUSINESS_TRIP_FIELDS.FROM_SLOT] === '上午' ? 'AM' : 'PM';
          const toDate = this.formatDate(travelTime[BUSINESS_TRIP_FIELDS.TO_DATE]);
          const toSlot = travelTime[BUSINESS_TRIP_FIELDS.TO_SLOT] === '上午' ? 'AM' : 'PM';

          const jumpUrl = `https://s1k3ix.aliwork.com/${this.authService.appType}/processDetail?formInstId=${instanceId}`;

          try {
            const resp = await this.attendanceService.approveFinish({
              userId: creatorId,
              bizType: 2,
              fromDate: `${fromDate} ${fromSlot}`,
              toDate: `${toDate} ${toSlot}`,
              durationUnit: 'halfDay',
              calculateModel: 1,
              tagName: '出差',
              approveId,
              jumpUrl,
            });

            if (resp.errcode === 0) {
              logger.log(
                `出差数据更新成功: ${creator} & ${creatorId} & ${fromDate} & ${toDate} & ${approveId}`,
              );
              syncedRecords.push({ userId: creatorId, name: creator, fromDate, toDate, approveId });
              count++;
            }
          } catch (error: any) {
            errors.push(`出差同步失败 ${creator}: ${error.message}`);
            logger.error(`出差数据更新出错: ${error.message}`);
          }
          index++;
        }
      }
    } catch (error: any) {
      errors.push(`出差同步整体失败: ${error.message}`);
    }

    logger.log(`----出差同步完成，共${count}条----`);

    return {
      success: errors.length === 0,
      processedCount: count,
      errors,
      duration: Date.now() - startTime,
      logs: logger.getText(),
      details: { syncedRecords },
    };
  }

  /**
   * 同步出差变更数据
   * 对应Python: business_trip_change_to_dingtalk()
   */
  async syncChanges(fromTime?: string, toTime?: string, userId?: string, externalLogger?: SyncLogger): Promise<SyncExecutionResult> {
    const logger = externalLogger || new SyncLogger('BusinessTripSyncService');
    const startTime = Date.now();
    let syncCount = 0;
    let cancelCount = 0;
    const errors: string[] = [];

    logger.log('----开始出差变更同步----');
    const { displayFrom: displayFrom2, displayTo: displayTo2 } = formatTimeDisplay(fromTime, toTime);
    logger.log(`处理区间: ${displayFrom2} ~ ${displayTo2}`);
    if (userId) logger.log(`指定员工: ${userId}`);

    // 收集所有同步记录用于最终验证
    const allSyncedRecords: { userId: string; name: string; fromDate: string; toDate: string; approveId: string }[] = [];

    try {
      // 先执行出差同步，保证数据一致
      logger.log('先执行出差基础同步...');
      const baseResult = await this.sync(fromTime, toTime, userId, logger);
      logger.log(`基础同步完成: 处理 ${baseResult.processedCount} 条, ${baseResult.errors.length} 个错误`);

      // 收集基础同步记录
      if (baseResult.details?.syncedRecords) {
        allSyncedRecords.push(...baseResult.details.syncedRecords);
      }

      const formData = await this.yidaService.searchApprovedInstances(
        FORM_UUIDS.BUSINESS_TRIP_CHANGE,
        fromTime,
        toTime,
        userId,
      );
      logger.log(`从宜搭获取到 ${formData.length} 条审批通过的出差变更申请${userId ? `（已按员工 ${userId} 过滤）` : ''}`);
      const fiveMonthsAgo = Date.now() / 1000 - 60 * 60 * 24 * 150;

      for (const data of formData) {
        const businessTripData = data.formData;
        const businessType = businessTripData[BUSINESS_TRIP_CHANGE_FIELDS.TYPE];
        const creatorId = data.creatorUserId;
        const creator = businessTripData[BUSINESS_TRIP_CHANGE_FIELDS.CREATOR];
        const serialNumber = getDingtalkSerialNumber(data);
        const instanceId = data.formInstanceId;

        if (!serialNumber) {
          errors.push(`出差变更同步缺少流水号: ${creator || creatorId} instanceId=${instanceId}`);
          logger.error(`出差变更单缺少 serial number，跳过: ${creator || creatorId} instanceId=${instanceId}`);
          continue;
        }

        let isCancel = false;
        let preSerialId = '';
        let preInstanceId = '';

        // 判断出差变更类型
        if (businessType === BUSINESS_TRIP_CHANGE_TYPES.CANCEL_UNCHANGED) {
          isCancel = true;
          const assoc = JSON.parse(JSON.parse(businessTripData[BUSINESS_TRIP_CHANGE_FIELDS.CANCEL_UNCHANGED_ASSOC]));
          preSerialId = assoc[0].title;
          preInstanceId = assoc[0].instanceId;
        } else if (businessType === BUSINESS_TRIP_CHANGE_TYPES.CANCEL_CHANGED) {
          isCancel = true;
          const assoc = JSON.parse(JSON.parse(businessTripData[BUSINESS_TRIP_CHANGE_FIELDS.CANCEL_CHANGED_ASSOC]));
          preSerialId = assoc[0].title;
          preInstanceId = assoc[0].instanceId;
        } else if (businessType === BUSINESS_TRIP_CHANGE_TYPES.FIRST_CHANGE) {
          const assoc = JSON.parse(JSON.parse(businessTripData[BUSINESS_TRIP_CHANGE_FIELDS.FIRST_CHANGE_ASSOC]));
          preSerialId = assoc[0].title;
          preInstanceId = assoc[0].instanceId;
        } else if (businessType === BUSINESS_TRIP_CHANGE_TYPES.NOT_FIRST_CHANGE) {
          const assoc = JSON.parse(JSON.parse(businessTripData[BUSINESS_TRIP_CHANGE_FIELDS.NOT_FIRST_CHANGE_ASSOC]));
          preSerialId = assoc[0].title;
          preInstanceId = assoc[0].instanceId;
        }

        // 取消原考勤数据
        try {
          const preData = await this.yidaService.getInstanceById(preInstanceId);
          // 注意: getInstanceById 不返回审批状态字段，
          // 变更单本身已通过 searchApprovedInstances 确认为审批通过
          const preTravelList = preData.formData?.[BUSINESS_TRIP_CHANGE_FIELDS.TABLE_FIELD] || [];
          let index = 1;
          for (const tempData of preTravelList) {
            if (tempData[BUSINESS_TRIP_CHANGE_FIELDS.FROM_DATE] / 1000 < fiveMonthsAgo) {
              index++;
              continue;
            }

            const preApproveId = `${preSerialId}-${index}`;
            const preFromDate = this.formatDate(tempData[BUSINESS_TRIP_CHANGE_FIELDS.FROM_DATE]);
            const preToDate = this.formatDate(tempData[BUSINESS_TRIP_CHANGE_FIELDS.TO_DATE]);
            const resp = await this.attendanceService.approveCancel(creatorId, preApproveId);
            index++;
            if (resp.errcode === 0) {
              logger.log(`取消成功: ${creator} & ${preFromDate} ~ ${preToDate} & ${preApproveId}`);
              cancelCount++;
            }
          }
        } catch (error: any) {
          errors.push(`取消原关联单失败 ${creator}: ${error.message}`);
          logger.error(`取消原关联单失败: ${error.message}`);
        }

        // 如果不是取消操作，同步新的出差数据
        if (!isCancel) {
          const allTravelTime = businessTripData[BUSINESS_TRIP_CHANGE_FIELDS.TABLE_FIELD] || [];
          let newIndex = 1;
          for (const travelTime of allTravelTime) {
            const approveId = `${serialNumber}-${newIndex}`;
            const fromDate = this.formatDate(travelTime[BUSINESS_TRIP_CHANGE_FIELDS.FROM_DATE]);
            const fromSlot = travelTime[BUSINESS_TRIP_CHANGE_FIELDS.FROM_SLOT] === '上午' ? 'AM' : 'PM';
            const toDate = this.formatDate(travelTime[BUSINESS_TRIP_CHANGE_FIELDS.TO_DATE]);
            const toSlot = travelTime[BUSINESS_TRIP_CHANGE_FIELDS.TO_SLOT] === '上午' ? 'AM' : 'PM';

            const jumpUrl = `https://s1k3ix.aliwork.com/${this.authService.appType}/processDetail?formInstId=${instanceId}`;

            try {
              const resp = await this.attendanceService.approveFinish({
                userId: creatorId,
                bizType: 2,
                fromDate: `${fromDate} ${fromSlot}`,
                toDate: `${toDate} ${toSlot}`,
                durationUnit: 'halfDay',
                calculateModel: 1,
                tagName: '出差',
                approveId,
                jumpUrl,
              });
              if (resp.errcode === 0) {
                logger.log(`变更同步成功: ${creator} & ${creatorId} & ${fromDate} & ${toDate} & ${approveId}`);
                allSyncedRecords.push({ userId: creatorId, name: creator, fromDate, toDate, approveId });
                syncCount++;
              }
            } catch (error: any) {
              errors.push(`出差变更同步新数据失败 ${creator}: ${error.message}`);
              logger.error(`出差变更同步新数据失败 ${creator}: ${error.message}`);
            }
            newIndex++;
          }
        }
      }
    } catch (error: any) {
      errors.push(`出差变更同步整体失败: ${error.message}`);
    }

    logger.log(`----出差变更同步完成，同步 ${syncCount} 条，取消 ${cancelCount} 条----`);

    // 全部同步完成后验证考勤状态
    if (allSyncedRecords.length > 0) {
      await this.verifyAttendanceStatus(allSyncedRecords, logger);
    }

    return {
      success: errors.length === 0,
      processedCount: syncCount + cancelCount,
      errors,
      duration: Date.now() - startTime,
      logs: logger.getText(),
    };
  }

  /**
   * 验证同步后的考勤状态
   * 对每个用户抽取出差起始日检查，确认"出差"记录存在且不重复
   */
  private async verifyAttendanceStatus(
    records: { userId: string; name: string; fromDate: string; toDate: string; approveId: string }[],
    logger: SyncLogger,
  ): Promise<void> {
    logger.log('----开始验证考勤状态----');

    // 按用户分组，每个用户只抽查出差起始日（避免大量API调用）
    const userDateMap = new Map<string, { name: string; dates: Set<string> }>();
    for (const r of records) {
      if (!userDateMap.has(r.userId)) {
        userDateMap.set(r.userId, { name: r.name, dates: new Set() });
      }
      userDateMap.get(r.userId)!.dates.add(r.fromDate);
    }

    let verifyOk = 0;
    let verifyFail = 0;

    for (const [uid, { name, dates }] of userDateMap) {
      // 每个用户最多抽查 3 个日期
      const checkDates = [...dates].slice(0, 3);
      for (const date of checkDates) {
        try {
          const resp = await this.attendanceService.getApproveRecords(uid, date);
          if (resp.errcode !== 0) {
            logger.error(`验证失败: ${name}(${uid}) ${date} API错误 errcode=${resp.errcode}`);
            verifyFail++;
            continue;
          }

          const approveList = resp.result?.approve_list || [];
          const tripRecords = approveList.filter((a: any) => a.tag_name === '出差');

          if (tripRecords.length === 0) {
            logger.error(`验证异常: ${name}(${uid}) ${date} 无出差记录！`);
            verifyFail++;
          } else if (tripRecords.length > 1) {
            logger.warn(`验证警告: ${name}(${uid}) ${date} 有 ${tripRecords.length} 条出差记录（可能重复）`);
            for (const t of tripRecords) {
              logger.warn(`  - procInst_id=${t.procInst_id} ${t.begin_time}~${t.end_time}`);
            }
            verifyFail++;
          } else {
            logger.log(`验证通过: ${name}(${uid}) ${date} 出差✓ procInst_id=${tripRecords[0].procInst_id}`);
            verifyOk++;
          }
        } catch (error: any) {
          logger.error(`验证异常: ${name}(${uid}) ${date} ${error.message}`);
          verifyFail++;
        }
      }
    }

    logger.log(`----验证完成: ${verifyOk} 通过, ${verifyFail} 异常----`);
  }

  private formatDate(timestamp: number): string {
    // 宜搭时间戳为北京时间，需用 Asia/Shanghai 时区格式化
    const date = new Date(timestamp);
    const formatter = new Intl.DateTimeFormat('sv-SE', { timeZone: 'Asia/Shanghai' });
    return formatter.format(date); // 'sv-SE' locale 输出 YYYY-MM-DD 格式
  }
}
