import { Injectable, Logger } from '@nestjs/common';
import { AdpAuthService } from './adp-auth.service';

export interface AdpWorker {
  associateOID: string;
  workerID?: { idValue?: string };
  workerStatus?: { statusCode?: { codeValue?: string } };
  person?: {
    legalName?: { givenName?: string; familyName1?: string };
  };
  businessCommunication?: {
    emails?: Array<{ itemID?: string; emailUri?: string }>;
  };
}

export interface AdpTimeOffEntry {
  payCode?: { codeValue?: string };
  entryStatusCode?: { codeValue?: string };
  dateTimePeriod?: { startDateTime?: string; endDateTime?: string };
  // 我们刻意不在内部模型里持久化 payCode/policyCode（隐私决策）
  timeOffPolicyCode?: { codeValue?: string };
}

export interface AdpTimeOffRequest {
  timeOffRequestID: string;
  associateOID: string;
  requestStatusCode?: { codeValue?: string };
  timeOffEntries?: AdpTimeOffEntry[];
}

export type AdpTimeOffStatus = 'approved' | 'pending' | 'cancelled';

const PAGE_SIZE = 50;

/**
 * 包装 ADP REST API 调用。
 * 不在此处做业务过滤；返回原始数据让上层处理。
 */
@Injectable()
export class AdpApiService {
  private readonly logger = new Logger(AdpApiService.name);

  constructor(private readonly auth: AdpAuthService) {}

  /**
   * 拉取所有 Active 员工。
   * /hr/v2/workers 不支持 $filter，分页拉全部后客户端过滤 statusCode='Active'。
   */
  async fetchAllActiveWorkers(): Promise<AdpWorker[]> {
    const client = await this.auth.createHttpClient();
    const all: AdpWorker[] = [];
    let skip = 0;

    while (true) {
      const url = `/hr/v2/workers?$top=${PAGE_SIZE}&$skip=${skip}`;
      const response = await client.get(url);
      const workers = (response.data?.workers ?? []) as AdpWorker[];

      if (workers.length === 0) break;

      const active = workers.filter(
        (w) => w.workerStatus?.statusCode?.codeValue === 'Active',
      );
      all.push(...active);

      if (workers.length < PAGE_SIZE) break;
      skip += PAGE_SIZE;

      // 安全阀：限制最多 100 页
      if (skip > 5000) {
        this.logger.warn('ADP fetchAllActiveWorkers 超过 5000 条，提前终止');
        break;
      }
    }

    this.logger.log(`ADP /hr/v2/workers: 拉到 ${all.length} 个 Active 员工`);
    return all;
  }

  /**
   * 拉取单个员工指定 status 的 PTO 请求。
   * /time/v3/workers/{aoid}/time-off-requests 必须带 $filter 三件套。
   * 返回为空（404 / 无数据）时返回空数组。
   */
  async fetchTimeOffRequests(
    aoid: string,
    status: AdpTimeOffStatus,
    startDate: Date,
    endDate: Date,
  ): Promise<AdpTimeOffRequest[]> {
    const client = await this.auth.createHttpClient();

    const startStr = this.formatDate(startDate);
    const endStr = this.formatDate(endDate);
    const filter = `datePeriod/startDate ge '${startStr}' and datePeriod/endDate le '${endStr}' and requestStatusCode/codeValue eq '${status}'`;
    const url = `/time/v3/workers/${aoid}/time-off-requests?$filter=${encodeURIComponent(filter)}`;

    // 429 重试：最多 3 次，遵循 Retry-After header（无则指数退避 2/4/8s）
    const MAX_RETRIES = 3;
    for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
      try {
        const response = await client.get(url);
        return (response.data?.timeOffRequests ?? []) as AdpTimeOffRequest[];
      } catch (error: any) {
        const httpStatus = error.response?.status;
        if (httpStatus === 404) return [];
        if (httpStatus === 429 && attempt < MAX_RETRIES) {
          const retryAfter = Number(error.response?.headers?.['retry-after']);
          const waitMs = Number.isFinite(retryAfter) && retryAfter > 0
            ? retryAfter * 1000
            : Math.pow(2, attempt + 1) * 1000;
          this.logger.warn(`ADP 429 限流 aoid=${aoid}，等待 ${waitMs}ms 后重试 (${attempt + 1}/${MAX_RETRIES})`);
          await new Promise((resolve) => setTimeout(resolve, waitMs));
          continue;
        }
        throw error;
      }
    }
    // attempt > MAX_RETRIES 时上面 catch 已 throw，理论不可达；保留兜底空数组防 TS 警告
    return [];
  }

  /** 抽取员工 Business email；ADP 端可能用 ff.com 或 faradayfuture.com */
  static extractWorkerEmail(worker: AdpWorker): string | null {
    const emails = worker.businessCommunication?.emails ?? [];
    const business = emails.find((e) => e.itemID === 'Business');
    if (business?.emailUri) return business.emailUri.toLowerCase();
    // fallback 第一个 email
    if (emails[0]?.emailUri) return emails[0].emailUri.toLowerCase();
    return null;
  }

  /** YYYY-MM-DD */
  private formatDate(d: Date): string {
    const year = d.getUTCFullYear();
    const month = String(d.getUTCMonth() + 1).padStart(2, '0');
    const day = String(d.getUTCDate()).padStart(2, '0');
    return `${year}-${month}-${day}`;
  }
}
