import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { SkipAssertAccess } from '@common/decorators/skip-assert-access.decorator';
import { AdpApiService } from '../sdk/adp-api.service';
import { AdpSyncExecutionResult } from './adp-sync-result';

const FF_DOMAINS = ['ff.com', 'faradayfuture.com'] as const;

/**
 * ADP Linker：把 system User.email 关联到 ADP associateOID。
 *
 * 决策（D1）：@ff.com 和 @faradayfuture.com 视为同一身份。
 * 实现：拆 email 的 local-part 做匹配，ADP 任一域名命中均算 link 成功。
 *
 * 决策（D2）：未匹配通过 AutomationExecution.logs + recordsUnmatched 体现，不建独立表。
 */
@Injectable()
export class AdpLinkerService {
  private readonly logger = new Logger(AdpLinkerService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly adpApi: AdpApiService,
  ) {}

  /**
   * 跑 linker。
   * @param dryRun true 时不写入 User.adpAoid，仅产出统计
   */
  @SkipAssertAccess('系统级同步任务：ADP 拉员工 + 按 email 匹配 + 写 adpAoid，无 user-supplied target ID 的 IDOR 风险；调度走 cron 或会议管理员手动触发')
  async run(dryRun = false): Promise<AdpSyncExecutionResult> {
    const startedAt = Date.now();
    const errors: string[] = [];
    const logLines: string[] = [];

    let workerCount = 0;
    let mappedUsers = 0;
    let alreadyLinked = 0;
    // 未匹配 = ADP 中存在、FF AI 找不到对应 User 的员工
    // （FF AI 比 ADP 多，反向统计无意义；ADP 一侧才是关注点）
    const unmatchedAdp: Array<{ aoid: string; email: string }> = [];

    try {
      const workers = await this.adpApi.fetchAllActiveWorkers();
      workerCount = workers.length;
      const localPartToAdp = new Map<string, { aoid: string; email: string }>();

      for (const w of workers) {
        const email = AdpApiService.extractWorkerEmail(w);
        if (!email || !w.associateOID) continue;
        const localPart = this.normalizeLocalPart(email);
        if (!localPart) continue;
        if (!localPartToAdp.has(localPart)) {
          localPartToAdp.set(localPart, { aoid: w.associateOID, email });
        }
      }

      logLines.push(`[ADP] 拉到 Active 员工 ${workerCount} 个，唯一 local-part ${localPartToAdp.size} 个`);

      const allActiveUsers = await this.prisma.user.findMany({
        where: { status: 'ACTIVE', email: { not: '' } },
        select: { id: true, email: true, adpAoid: true },
      });
      const localPartToUser = new Map<string, { id: string; email: string; adpAoid: string | null }>();
      for (const u of allActiveUsers) {
        const lp = this.normalizeLocalPart(u.email);
        if (!lp) continue;
        if (!localPartToUser.has(lp)) localPartToUser.set(lp, u);
      }

      alreadyLinked = allActiveUsers.filter((u) => u.adpAoid).length;

      // 以 ADP 为主导：FF AI 通常比 ADP 多人（外包、Bot 等），反向统计无意义
      const now = new Date();
      for (const [localPart, adp] of localPartToAdp) {
        const user = localPartToUser.get(localPart);
        if (!user) {
          unmatchedAdp.push({ aoid: adp.aoid, email: adp.email });
          continue;
        }
        // 已经 link 过同一个 aoid 跳过
        if (user.adpAoid === adp.aoid) continue;

        if (!dryRun) {
          try {
            await this.prisma.user.update({
              where: { id: user.id },
              data: { adpAoid: adp.aoid, adpLinkedAt: now },
            });
          } catch (e: any) {
            errors.push(`user=${user.id} email=${user.email} aoid=${adp.aoid} 写入失败: ${e.message}`);
            continue;
          }
        }
        mappedUsers++;
      }

      logLines.push(
        `[ADP] 本次新匹配 ${mappedUsers} 个；ADP 中 ${unmatchedAdp.length} 个员工在 FF AI 找不到对应 User`,
      );

      if (unmatchedAdp.length > 0 && unmatchedAdp.length <= 50) {
        logLines.push('[ADP] 未匹配明细（ADP 一侧）：');
        for (const u of unmatchedAdp) {
          logLines.push(`  - aoid=${u.aoid} email=${u.email}`);
        }
      } else if (unmatchedAdp.length > 50) {
        logLines.push(`[ADP] 未匹配明细（ADP 一侧，前 50 条）：`);
        for (const u of unmatchedAdp.slice(0, 50)) {
          logLines.push(`  - aoid=${u.aoid} email=${u.email}`);
        }
        logLines.push(`  ... 还有 ${unmatchedAdp.length - 50} 条未列出`);
      }

      return {
        success: true,
        duration: Date.now() - startedAt,
        errors,
        logs: logLines.join('\n'),
        recordsFetched: workerCount,
        recordsUpserted: mappedUsers,
        recordsUnmatched: unmatchedAdp.length,
        extras: {
          alreadyLinkedCount: alreadyLinked,
          dryRun,
          unmatchedAdp,
        },
      };
    } catch (error: any) {
      this.logger.error(`Linker 运行失败: ${error.message}`);
      errors.push(error.message);
      logLines.push(`[ERROR] ${error.message}`);
      return {
        success: false,
        duration: Date.now() - startedAt,
        errors,
        logs: logLines.join('\n'),
        recordsFetched: workerCount,
      };
    }
  }

  /** email 拆 local-part；要求是 @ff.com 或 @faradayfuture.com（其他域名视为不可链接） */
  private normalizeLocalPart(email: string): string | null {
    const e = (email || '').trim().toLowerCase();
    const at = e.indexOf('@');
    if (at <= 0) return null;
    const local = e.slice(0, at);
    const domain = e.slice(at + 1);
    if (!FF_DOMAINS.includes(domain as any)) return null;
    return local;
  }
}
