import { Injectable, Logger } from '@nestjs/common';
import { DingtalkAuthService } from './dingtalk-auth.service';
import axios from 'axios';

@Injectable()
export class DingtalkAttendanceService {
  private readonly logger = new Logger(DingtalkAttendanceService.name);
  private static readonly LEAVE_QUOTA_BATCH_SIZE = 50;
  private static readonly LEAVE_QUOTA_PAGE_SIZE = 50;
  private static readonly API_THROTTLE_MS = 200;

  constructor(private authService: DingtalkAuthService) {}

  private async throttle(): Promise<void> {
    await new Promise((resolve) => setTimeout(resolve, DingtalkAttendanceService.API_THROTTLE_MS));
  }

  /**
   * 通知钉钉审批已完成（出差/外勤/加班）
   * 对应Python: DingtalkUtils.business_trip_approve_finish_to_dingtalk()
   */
  async approveFinish(params: {
    userId: string;
    bizType: number; // 1:加班 2:出差/外出 3:请假
    fromDate: string;
    toDate: string;
    durationUnit: string; // 'day'|'halfDay'|'hour'
    calculateModel: number; // 0:自然日 1:工作日
    tagName: string; // '出差'|'外出'|'加班'
    approveId: string;
    jumpUrl: string;
    subType?: string;
    overtimeDuration?: number;
    overtimeToMore?: number;
  }): Promise<any> {
    await this.throttle();
    const token = await this.authService.getAccessToken();

    this.logger.log(`[API→] approveFinish userId=${params.userId} tag=${params.tagName} approveId=${params.approveId} ${params.fromDate}~${params.toDate}`);
    const requestBody: Record<string, any> = {
      userid: params.userId,
      biz_type: params.bizType,
      from_time: params.fromDate,
      to_time: params.toDate,
      duration_unit: params.durationUnit,
      calculate_model: params.calculateModel,
      tag_name: params.tagName,
      approve_id: params.approveId,
      jump_url: params.jumpUrl,
    };

    if (params.subType) requestBody.sub_type = params.subType;
    if (params.bizType === 1) {
      requestBody.overtime_duration = params.overtimeDuration || '';
      requestBody.overtime_to_more = params.overtimeToMore || 1;
    }

    const resp = await axios.post(
      'https://oapi.dingtalk.com/topapi/attendance/approve/finish',
      requestBody,
      { params: { access_token: token } },
    );
    this.logger.log(`[API←] approveFinish approveId=${params.approveId} errcode=${resp.data.errcode} errmsg=${resp.data.errmsg}`);

    return resp.data;
  }

  /**
   * 取消考勤审批
   * 对应Python: DingtalkUtils.business_trip_cancel_finish_to_dingtalk()
   */
  async approveCancel(userId: string, approveId: string): Promise<any> {
    await this.throttle();
    const token = await this.authService.getAccessToken();

    this.logger.log(`[API→] approveCancel userId=${userId} approveId=${approveId}`);
    const resp = await axios.post(
      'https://oapi.dingtalk.com/topapi/attendance/approve/cancel',
      { userid: userId, approve_id: approveId },
      { params: { access_token: token } },
    );
    this.logger.log(`[API←] approveCancel approveId=${approveId} errcode=${resp.data.errcode} errmsg=${resp.data.errmsg}`);

    return resp.data;
  }

  /**
   * 查询假期余额
   * 对应Python: DingtalkUtils.search_leave_quota()
   */
  async searchLeaveQuota(leaveCode: string, userIds: string | string[]): Promise<any[]> {
    const normalizedUserIds = this.normalizeUserIds(userIds);
    if (normalizedUserIds.length === 0) {
      this.logger.warn(`searchLeaveQuota 跳过空用户列表 leaveCode=${leaveCode}`);
      return [];
    }

    const result: any[] = [];
    for (let index = 0; index < normalizedUserIds.length; index += DingtalkAttendanceService.LEAVE_QUOTA_BATCH_SIZE) {
      const batch = normalizedUserIds.slice(index, index + DingtalkAttendanceService.LEAVE_QUOTA_BATCH_SIZE);
      const batchResult = await this.searchLeaveQuotaBatch(leaveCode, batch);
      result.push(...batchResult);
    }

    return result;
  }

  private async searchLeaveQuotaBatch(leaveCode: string, userIds: string[]): Promise<any[]> {
    const token = await this.authService.getAccessToken();
    const result: any[] = [];
    let offset = 0;
    const size = DingtalkAttendanceService.LEAVE_QUOTA_PAGE_SIZE;
    const userIdsArg = userIds.join(',');

    while (true) {
      this.logger.log(`[API→] searchLeaveQuota leaveCode=${leaveCode} userCount=${userIds.length} offset=${offset}`);
      const resp = await axios.post(
        'https://oapi.dingtalk.com/topapi/attendance/vacation/quota/list',
        {
          op_userid: this.authService.operatorId,
          userids: userIdsArg,
          offset,
          size,
          leave_code: leaveCode,
        },
        { params: { access_token: token } },
      );
      this.logger.log(`[API←] searchLeaveQuota errcode=${resp.data.errcode} count=${resp.data.result?.leave_quotas?.length || 0}`);

      if (resp.data.errcode === 0) {
        const leaveQuotas = Array.isArray(resp.data.result?.leave_quotas)
          ? resp.data.result.leave_quotas
          : [];
        result.push(...leaveQuotas);
        if (!resp.data.result?.has_more) break;
        offset += size;
      } else {
        if (resp.data.errcode === 41 && userIds.length > 1) {
          this.logger.warn(`searchLeaveQuota 批量查询失败，回退为单人查询 leaveCode=${leaveCode} userCount=${userIds.length}`);
          return this.searchLeaveQuotaOneByOne(leaveCode, userIds);
        }
        this.logger.error(`searchLeaveQuota错误: ${resp.data.errmsg}`);
        break;
      }
    }

    return result;
  }

  private async searchLeaveQuotaOneByOne(leaveCode: string, userIds: string[]): Promise<any[]> {
    const result: any[] = [];
    for (const userId of userIds) {
      const quotas = await this.searchLeaveQuotaBatch(leaveCode, [userId]);
      result.push(...quotas);
    }
    return result;
  }

  private normalizeUserIds(userIds: string | string[]): string[] {
    const values = Array.isArray(userIds) ? userIds : userIds.split(',');
    return values
      .map(value => String(value || '').trim())
      .filter(Boolean);
  }

  /**
   * 获取企业所有假期类型列表
   * 钉钉 API: /topapi/attendance/vacation/type/list
   */
  async listLeaveTypes(): Promise<Array<{ leave_code: string; leave_name: string; [key: string]: any }>> {
    const token = await this.authService.getAccessToken();
    this.logger.log('[API→] listLeaveTypes');
    const resp = await axios.post(
      'https://oapi.dingtalk.com/topapi/attendance/vacation/type/list',
      { op_userid: this.authService.operatorId, vacation_source: 'all' },
      { params: { access_token: token } },
    );
    this.logger.log(`[API←] listLeaveTypes errcode=${resp.data.errcode} count=${resp.data.result?.length || 0}`);

    if (resp.data.errcode !== 0) {
      this.logger.error(`listLeaveTypes 错误: ${resp.data.errmsg}`);
      return [];
    }

    return Array.isArray(resp.data.result) ? resp.data.result : [];
  }

  /**
   * 批量更新假期余额
   * 对应Python: DingtalkUtils.update_leave_quota()
   *
   * 重要：quota_num_per_day 表示的是【目标总额】，不是增减差值。
   * - cal_type: 'add' + quota_num_per_day: 500 → 总额设为当前值+5天
   * - cal_type: 'sub' + quota_num_per_day: 0   → 总额设为0（清零）
   * - cal_type: 'sub' + quota_num_per_day: 300 → 无效果（不会减3天）
   * 所以要减少配额，应使用 cal_type: 'sub' + quota_num_per_day: 0 来清零，
   * 或用 cal_type: 'add' + 目标值来重设。
   *
   * 必须传 quota_cycle、start_time、end_time，否则会静默失败（errcode 仍返回 0，
   * 但 result 数组中会包含错误原因）。
   */
  async updateLeaveQuota(leaveQuotas: any[]): Promise<any> {
    const token = await this.authService.getAccessToken();

    const resp = await axios.post(
      'https://oapi.dingtalk.com/topapi/attendance/vacation/quota/update',
      {
        leave_quotas: leaveQuotas,
        op_userid: this.authService.operatorId,
      },
      { params: { access_token: token } },
    );

    this.logger.log(`[API←] updateLeaveQuota errcode=${resp.data.errcode} count=${leaveQuotas.length}`);
    if (resp.data.errcode !== 0) {
      this.logger.error(`假期余额更新异常: ${JSON.stringify(resp.data)}`);
    }

    return resp.data;
  }

  /**
   * 查询用户某日的考勤审批记录（出差/外出/加班等）
   * 用于同步后验证考勤状态
   */
  async getApproveRecords(userId: string, workDate: string): Promise<any> {
    await this.throttle();
    const token = await this.authService.getAccessToken();

    this.logger.log(`[API→] getUpdateData userId=${userId} workDate=${workDate}`);
    const resp = await axios.post(
      'https://oapi.dingtalk.com/topapi/attendance/getupdatedata',
      { userid: userId, work_date: `${workDate} 00:00:00` },
      { params: { access_token: token } },
    );
    this.logger.log(`[API←] getUpdateData errcode=${resp.data.errcode} approve_list=${resp.data.result?.approve_list?.length || 0}`);

    if (resp.data.errcode !== 0) {
      this.logger.error(`getUpdateData 错误: errcode=${resp.data.errcode} errmsg=${resp.data.errmsg}`);
    } else if (resp.data.result?.approve_list?.length > 0) {
      // 记录完整审批记录便于调试
      for (const item of resp.data.result.approve_list) {
        this.logger.log(`[API←] approve_item: ${JSON.stringify(item).slice(0, 300)}`);
      }
    }

    return resp.data;
  }

  /**
   * 获取假期变更记录（新版 API v1.0）
   * 包含：请假消费记录 + 配额变更记录（释放/撤销/初始化）
   * 新版比旧版多返回 gmtCreate（操作时间）、gmtModified、opUserId（操作人）
   * @param leaveCode 假期类型
   * @param userIds 员工 userId 数组，每次最多 50 人
   */
  async getLeaveRecords(leaveCode: string, userIds: string | string[]): Promise<any[]> {
    const token = await this.authService.getAccessToken();
    const records: any[] = [];
    const userIdArray = Array.isArray(userIds) ? userIds : userIds.split(',').map(s => s.trim()).filter(Boolean);
    let pageNumber = 0; // 钉钉文档：首次传 0，后续为偏移量累积值
    const pageSize = 200; // 新版 API 最大 200

    while (true) {
      try {
        this.logger.log(`[API→] getLeaveRecords(v1.0) leaveCode=${leaveCode} users=${userIdArray.length} offset=${pageNumber}`);
        const resp = await axios.post(
          'https://api.dingtalk.com/v1.0/attendance/vacations/records/query',
          {
            opUserId: this.authService.operatorId,
            leaveCode,
            userIds: userIdArray,
            pageNumber,
            pageSize,
          },
          {
            headers: {
              'x-acs-dingtalk-access-token': token,
              'Content-Type': 'application/json',
            },
          },
        );

        const result = resp.data?.result;
        const leaveRecords = result?.leaveRecords || [];
        this.logger.log(`[API←] getLeaveRecords(v1.0) count=${leaveRecords.length} hasMore=${result?.hasMore}`);

        // 记录返回体样本（首批首条）
        if (pageNumber === 1 && leaveRecords.length > 0 && records.length === 0) {
          this.logger.log(`[API←] getLeaveRecords sample: ${JSON.stringify(leaveRecords[0]).slice(0, 600)}`);
        }
        records.push(...leaveRecords);

        if (result?.hasMore) {
          pageNumber += pageSize; // offset 累加
        } else {
          break;
        }
      } catch (error: any) {
        this.logger.error(`获取假期变更记录出错: ${error.message} response=${JSON.stringify(error.response?.data || {}).slice(0, 500)}`);
        break;
      }
    }

    return records;
  }
}
