import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { DingtalkHrmService } from './sdk/dingtalk-hrm.service';
import * as fs from 'fs';
import * as path from 'path';

const DEPT_CACHE_FILE = path.join(__dirname, '..', '..', '..', '..', '..', 'data', 'departments-cache.json');

@Injectable()
export class EmployeeManagementService {
  private readonly logger = new Logger(EmployeeManagementService.name);
  private departmentCache: Record<number, { name: string; firstLevel: string }> | null = null;

  constructor(
    private prisma: PrismaService,
    private hrmService: DingtalkHrmService,
  ) {}

  /**
   * 从钉钉 API 拉取部门数据并缓存
   */
  async syncDepartments(): Promise<Record<number, { name: string; firstLevel: string }>> {
    this.logger.log('开始从钉钉同步部门数据...');
    const departmentDict = await this.hrmService.getDepartmentFullNames();
    this.logger.log(`获取到 ${Object.keys(departmentDict).length} 个部门`);
    this.departmentCache = departmentDict;

    // 写入缓存
    const dir = path.dirname(DEPT_CACHE_FILE);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
    fs.writeFileSync(DEPT_CACHE_FILE, JSON.stringify({
      updatedAt: new Date().toISOString(),
      departments: departmentDict,
    }, null, 2), 'utf-8');

    return departmentDict;
  }

  /**
   * 读取缓存的部门数据，优先命中内存，回退到磁盘缓存文件
   */
  private loadDepartmentCache(): Record<number, { name: string; firstLevel: string }> {
    if (this.departmentCache) return this.departmentCache;
    try {
      if (fs.existsSync(DEPT_CACHE_FILE)) {
        const data = JSON.parse(fs.readFileSync(DEPT_CACHE_FILE, 'utf-8'));
        this.departmentCache = data.departments || {};
        return this.departmentCache ?? {};
      }
    } catch {
      // 缓存损坏
    }
    return {};
  }

  /**
   * 从钉钉 API 拉取员工数据到本地 dingtalkEmployee 表
   */
  async syncEmployeesFromDingtalk(options?: { syncDepartments?: boolean }): Promise<{
    success: boolean;
    total: number;
    created: number;
    updated: number;
    terminated: number;
    errors: string[];
    duration: number;
  }> {
    const startTime = Date.now();
    let created = 0;
    let updated = 0;
    let terminated = 0;
    const errors: string[] = [];

    this.logger.log('开始从钉钉同步员工数据到本地表...');

    try {
      // 1. 获取在职员工 ID
      const employeeIds = await this.hrmService.getOnJobEmployeeIds();
      this.logger.log(`钉钉在职员工: ${employeeIds.length} 人`);

      // 2. 获取员工详情
      const infoMap = await this.hrmService.getEmployeeInfoByIds(employeeIds);
      this.logger.log(`获取到 ${Object.keys(infoMap).length} 人的详细信息`);

      // 3. 部门数据：手动同步时刷新，定时同步用缓存
      let departmentDict: Record<number, { name: string; firstLevel: string }>;
      if (options?.syncDepartments) {
        departmentDict = await this.syncDepartments();
      } else {
        departmentDict = this.loadDepartmentCache();
        if (Object.keys(departmentDict).length === 0) {
          // 缓存为空，必须拉取一次
          departmentDict = await this.syncDepartments();
        }
      }

      const dingtalkUserIds = new Set<string>();

      // 4. upsert 每个员工
      for (const userId of employeeIds) {
        const employee = infoMap[userId];
        if (!employee) continue;

        dingtalkUserIds.add(userId);

        try {
          const parsed = this.parseEmployeeFields(employee, departmentDict);
          const employeeRepo = (this.prisma as any).dingtalkEmployee;

          const existing = await employeeRepo.findUnique({
            where: { userId },
          });

          if (existing) {
            const wasTerminated = existing.status === '已离职';
            const updateData: Record<string, any> = {
              name: parsed.name,
              employeeNumber: parsed.employeeNumber,
              department: parsed.department,
              position: parsed.position,
              rank: parsed.rank,
              phone: parsed.phone,
              country: parsed.country,
              workPlace: parsed.workPlace,
              contractCompany: parsed.contractCompany,
              bank: parsed.bank,
              bankCardLast4: parsed.bankCardLast4,
              monthlyStandardHours: parsed.monthlyStandardHours,
              workHourLimit: parsed.workHourLimit,
              joinDate: parsed.joinDate,
              workStartDate: parsed.workStartDate,
              lastSyncedAt: new Date(),
            };

            if (wasTerminated) {
              updateData.status = '正常';
            }

            await employeeRepo.update({
              where: { userId },
              data: updateData,
            });
            updated++;

            if (wasTerminated && parsed.joinDate) {
              await this.addRejoinPeriod(userId, parsed.joinDate);
              this.logger.log(`再入职新增在职段: ${parsed.name || userId} (${userId}) joinDate=${parsed.joinDate.toISOString().slice(0, 10)}`);
            } else if (parsed.joinDate) {
              await this.ensureInitialPeriod(userId, parsed.joinDate);
            }
          } else {
            await employeeRepo.create({
              data: {
                userId,
                name: parsed.name,
                employeeNumber: parsed.employeeNumber,
                department: parsed.department,
                position: parsed.position,
                rank: parsed.rank,
                phone: parsed.phone,
                country: parsed.country,
                workPlace: parsed.workPlace,
                contractCompany: parsed.contractCompany,
                bank: parsed.bank,
                bankCardLast4: parsed.bankCardLast4,
                monthlyStandardHours: parsed.monthlyStandardHours,
                workHourLimit: parsed.workHourLimit,
                joinDate: parsed.joinDate,
                workStartDate: parsed.workStartDate,
                status: '正常',
                lastSyncedAt: new Date(),
              },
            });
            created++;

            if (parsed.joinDate) {
              await this.ensureInitialPeriod(userId, parsed.joinDate);
            }
          }
        } catch (error: any) {
          errors.push(`同步员工 ${userId} 失败: ${error.message}`);
          this.logger.error(`同步员工 ${userId} 失败: ${error.message}`);
        }
      }

      // 5. 标记离职
      try {
        const employeeRepo = (this.prisma as any).dingtalkEmployee;
        const localActiveEmployees = await employeeRepo.findMany({
          where: { status: '正常' },
          select: { userId: true, name: true },
        });

        for (const local of localActiveEmployees) {
          if (!dingtalkUserIds.has(local.userId)) {
            const leaveDate = new Date();
            await employeeRepo.update({
              where: { userId: local.userId },
              data: { status: '已离职', lastSyncedAt: new Date() },
            });
            const closed = await this.closeOpenPeriods(local.userId, leaveDate);
            terminated++;
            this.logger.log(
              `标记离职: ${local.name || local.userId} (${local.userId}) 关闭在职段 ${closed} 条`,
            );
          }
        }
      } catch (error: any) {
        errors.push(`标记离职员工失败: ${error.message}`);
        this.logger.error(`标记离职员工失败: ${error.message}`);
      }

      this.logger.log(
        `员工同步完成: 新增 ${created}, 更新 ${updated}, 标记离职 ${terminated}, 错误 ${errors.length}`,
      );
    } catch (error: any) {
      errors.push(`员工同步整体失败: ${error.message}`);
      this.logger.error(`员工同步整体失败: ${error.message}`);
    }

    return {
      success: errors.length === 0,
      total: created + updated,
      created,
      updated,
      terminated,
      errors,
      duration: Date.now() - startTime,
    };
  }

  /**
   * 获取员工列表（分页、搜索、筛选）
   */
  async getEmployees(params: {
    keyword?: string;
    status?: string;
    page?: number;
    pageSize?: number;
  }): Promise<{
    items: any[];
    total: number;
    page: number;
    pageSize: number;
  }> {
    const page = params.page || 1;
    const pageSize = params.pageSize || 50;
    const employeeRepo = (this.prisma as any).dingtalkEmployee;

    const where: Record<string, any> = {};

    if (params.status) {
      const statuses = params.status.split(',').map((s: string) => s.trim()).filter(Boolean);
      where.status = statuses.length === 1 ? statuses[0] : { in: statuses };
    }

    if (params.keyword?.trim()) {
      const keyword = params.keyword.trim();
      where.OR = [
        { name: { contains: keyword } },
        { employeeNumber: { contains: keyword } },
        { userId: { contains: keyword } },
      ];
    }

    const [items, total] = await Promise.all([
      employeeRepo.findMany({
        where,
        orderBy: [{ name: 'asc' }, { userId: 'asc' }],
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      employeeRepo.count({ where }),
    ]);

    return { items, total, page, pageSize };
  }

  /**
   * 更新员工信息（仅状态）
   */
  async updateEmployee(
    userId: string,
    data: { status?: string },
  ): Promise<any> {
    const employeeRepo = (this.prisma as any).dingtalkEmployee;

    const existing = await employeeRepo.findUnique({ where: { userId } });
    if (!existing) {
      return { success: false, error: `员工 ${userId} 不存在` };
    }

    const updateData: Record<string, any> = {};
    if (data.status !== undefined) {
      updateData.status = data.status;
    }

    if (Object.keys(updateData).length === 0) {
      return { success: true, message: '无需更新', employee: existing };
    }

    const updated = await employeeRepo.update({
      where: { userId },
      data: updateData,
    });

    this.logger.log(`手动更新员工 ${existing.name} (${userId}): ${JSON.stringify(updateData)}`);
    return { success: true, employee: updated };
  }

  /**
   * 解析钉钉员工的 field_data_list
   */
  private parseEmployeeFields(
    employee: any,
    departmentDict: Record<number, { name: string; firstLevel: string }>,
  ): {
    name: string;
    employeeNumber: string;
    department: string;
    position: string;
    rank: string;
    phone: string;
    country: string;
    workPlace: string;
    contractCompany: string;
    bank: string;
    bankCardLast4: string;
    monthlyStandardHours: string;
    workHourLimit: string;
    joinDate: Date | null;
    workStartDate: Date | null;
  } {
    const fields = employee.field_data_list || [];

    const getFieldValue = (fieldName: string): string => {
      const field = fields.find((item: any) => item.field_name === fieldName);
      return String(field?.field_value_list?.[0]?.value || '').trim();
    };

    let department = '';
    const deptField = fields.find((item: any) => item.field_name === '部门id');
    if (deptField) {
      const deptIds = (deptField.field_value_list?.[0]?.value || '')
        .split('|')
        .filter(Boolean);
      department = deptIds
        .map((id: string) => departmentDict[parseInt(id)]?.name || '')
        .filter(Boolean)
        .join('#');
    }

    return {
      name: getFieldValue('姓名'),
      employeeNumber: getFieldValue('工号'),
      department,
      position: getFieldValue('职位'),
      rank: getFieldValue('岗位职级'),
      phone: getFieldValue('手机号'),
      country: getFieldValue('国家'),
      workPlace: getFieldValue('办公地点'),
      contractCompany: getFieldValue('合同公司'),
      bank: getFieldValue('开户行'),
      bankCardLast4: getFieldValue('银行卡号后四位'),
      monthlyStandardHours: getFieldValue('全月标准工时'),
      workHourLimit: getFieldValue('工时上限'),
      joinDate: this.parseDateString(getFieldValue('入职时间')),
      workStartDate: this.parseDateString(getFieldValue('首次参加工作时间')),
    };
  }

  private parseDateString(value: string): Date | null {
    if (!value) return null;
    const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
    if (!match) return null;
    const [, year, month, day] = match;
    return new Date(Number(year), Number(month) - 1, Number(day));
  }

  // ========== 员工在职周期（支持离职再入职） ==========

  async getEmploymentPeriods(userId: string) {
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    return repo.findMany({ where: { userId }, orderBy: { periodIndex: 'asc' } });
  }

  async addEmploymentPeriod(userId: string, data: {
    joinDate: string;
    leaveDate?: string | null;
    countInTenure?: boolean;
    note?: string;
  }) {
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    const existing = await repo.findMany({ where: { userId }, orderBy: { periodIndex: 'desc' }, take: 1 });
    const nextIndex = existing.length > 0 ? existing[0].periodIndex + 1 : 1;
    const result = await repo.create({
      data: {
        userId,
        periodIndex: nextIndex,
        joinDate: new Date(data.joinDate),
        leaveDate: data.leaveDate ? new Date(data.leaveDate) : null,
        countInTenure: data.countInTenure ?? true,
        note: data.note || '',
      },
    });
    await this.calculateTenureDays(userId); // 写回缓存
    return result;
  }

  async updateEmploymentPeriod(id: string, data: {
    joinDate?: string;
    leaveDate?: string | null;
    countInTenure?: boolean;
    note?: string;
  }) {
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    const update: any = {};
    if (data.joinDate !== undefined) update.joinDate = new Date(data.joinDate);
    if (data.leaveDate !== undefined) update.leaveDate = data.leaveDate ? new Date(data.leaveDate) : null;
    if (data.countInTenure !== undefined) update.countInTenure = data.countInTenure;
    if (data.note !== undefined) update.note = data.note;
    const result = await repo.update({ where: { id }, data: update });
    await this.calculateTenureDays(result.userId); // 写回缓存
    return result;
  }

  async deleteEmploymentPeriod(id: string) {
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    const existing = await repo.findUnique({ where: { id } });
    const result = await repo.delete({ where: { id } });
    if (existing) await this.calculateTenureDays(existing.userId); // 写回缓存
    return result;
  }

  /**
   * 计算累计司龄（天数）
   * 口径：Σ countInTenure 在职段天数（leaveDate − joinDate）
   * 多次入职之间的 gap 自动排除（不属于任何在职段）。
   * 停薪留职**不扣减**司龄（HR 口径，2026-05-08 确认）；停薪只影响"年假是否释放"。
   * 有 employment_periods 记录 → 用累计计算
   * 无记录 → 回退到 join_date 简单计算
   */
  async calculateTenureDays(userId: string, asOfDate: Date = new Date()): Promise<{
    totalDays: number;
    periods: Array<{ joinDate: Date; leaveDate: Date | null; days: number; countInTenure: boolean; note: string }>;
    source: 'periods' | 'joinDate';
  }> {
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    const periods = await repo.findMany({
      where: { userId },
      orderBy: { periodIndex: 'asc' },
    });

    let totalDays = 0;
    let detail: any[] = [];
    let source: 'periods' | 'joinDate' = 'periods';

    if (periods.length === 0) {
      const emp = await (this.prisma as any).dingtalkEmployee.findUnique({ where: { userId } });
      source = 'joinDate';
      if (emp?.joinDate) {
        totalDays = Math.max(0, Math.floor((asOfDate.getTime() - emp.joinDate.getTime()) / 86400000));
        detail = [{ joinDate: emp.joinDate, leaveDate: null, days: totalDays, countInTenure: true, note: '从 join_date 回退' }];
      }
    } else {
      detail = periods.map((p: any) => {
        const end = p.leaveDate ? p.leaveDate : asOfDate;
        const days = Math.max(0, Math.floor((end.getTime() - p.joinDate.getTime()) / 86400000));
        return {
          joinDate: p.joinDate,
          leaveDate: p.leaveDate,
          days,
          countInTenure: p.countInTenure,
          note: p.note || '',
        };
      });
      totalDays = detail
        .filter((d: any) => d.countInTenure)
        .reduce((sum: number, d: any) => sum + d.days, 0);
    }

    // 写回缓存字段
    try {
      await (this.prisma as any).dingtalkEmployee.update({
        where: { userId },
        data: { tenureDays: totalDays },
      });
    } catch {
      /* 员工可能不存在，忽略 */
    }

    return { totalDays, periods: detail, source };
  }

  /**
   * 批量重算所有员工的司龄缓存（用于定时任务或手动刷新）
   */
  async recalculateAllTenure(): Promise<{ updated: number }> {
    const employees = await (this.prisma as any).dingtalkEmployee.findMany({
      select: { userId: true },
    });
    let updated = 0;
    for (const emp of employees) {
      await this.calculateTenureDays(emp.userId);
      updated++;
    }
    return { updated };
  }

  /**
   * 新员工同步时，若还没有 employment_periods 记录则建立第 1 段
   */
  private async ensureInitialPeriod(userId: string, joinDate: Date): Promise<void> {
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    const count = await repo.count({ where: { userId } });
    if (count > 0) return;
    await repo.create({
      data: {
        userId,
        periodIndex: 1,
        joinDate,
        leaveDate: null,
        countInTenure: true,
        note: '自动创建（新员工同步）',
      },
    });
    await this.calculateTenureDays(userId);
  }

  /**
   * 再入职：在原有周期后追加新段
   */
  private async addRejoinPeriod(userId: string, joinDate: Date): Promise<void> {
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    const last = await repo.findFirst({
      where: { userId },
      orderBy: { periodIndex: 'desc' },
    });
    const nextIndex = last ? last.periodIndex + 1 : 1;
    await repo.create({
      data: {
        userId,
        periodIndex: nextIndex,
        joinDate,
        leaveDate: null,
        countInTenure: true,
        note: '自动创建（再入职同步）',
      },
    });
    await this.calculateTenureDays(userId);
  }

  /**
   * 离职时关闭当前未闭合的在职段。
   * 只更新 leaveDate 为空的记录，保留历史段的原始 leaveDate。
   * 正常情况下最多 1 条；若存在多条未闭合段（历史脏数据），全部填上以保持自洽。
   */
  private async closeOpenPeriods(userId: string, leaveDate: Date): Promise<number> {
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    const openPeriods = await repo.findMany({
      where: { userId, leaveDate: null },
    });
    if (openPeriods.length === 0) {
      this.logger.warn(`标记离职 ${userId}：无未闭合的在职段，跳过 leaveDate 回填`);
      return 0;
    }
    if (openPeriods.length > 1) {
      this.logger.warn(`标记离职 ${userId}：检测到 ${openPeriods.length} 条未闭合在职段（数据异常）`);
    }
    for (const p of openPeriods) {
      await repo.update({
        where: { id: p.id },
        data: { leaveDate },
      });
    }
    await this.calculateTenureDays(userId);
    return openPeriods.length;
  }

  // ========== 停薪留职 ==========

  async getSuspensionPeriods(userId: string) {
    const repo = (this.prisma as any).dingtalkEmployeeSuspensionPeriod;
    return repo.findMany({
      where: { userId },
      orderBy: { startDate: 'asc' },
    });
  }

  async addSuspensionPeriod(userId: string, data: {
    startDate: string;
    endDate?: string | null;
    reason?: string;
    note?: string;
  }) {
    const repo = (this.prisma as any).dingtalkEmployeeSuspensionPeriod;
    return repo.create({
      data: {
        userId,
        startDate: new Date(data.startDate),
        endDate: data.endDate ? new Date(data.endDate) : null,
        reason: data.reason || '',
        note: data.note || '',
      },
    });
  }

  async updateSuspensionPeriod(id: string, data: {
    startDate?: string;
    endDate?: string | null;
    reason?: string;
    note?: string;
  }) {
    const repo = (this.prisma as any).dingtalkEmployeeSuspensionPeriod;
    const update: any = {};
    if (data.startDate !== undefined) update.startDate = new Date(data.startDate);
    if (data.endDate !== undefined) update.endDate = data.endDate ? new Date(data.endDate) : null;
    if (data.reason !== undefined) update.reason = data.reason;
    if (data.note !== undefined) update.note = data.note;
    return repo.update({ where: { id }, data: update });
  }

  async deleteSuspensionPeriod(id: string) {
    const repo = (this.prisma as any).dingtalkEmployeeSuspensionPeriod;
    return repo.delete({ where: { id } });
  }

  /**
   * 初始化员工在职周期表：为所有现有员工创建第 1 段记录
   */
  async initEmploymentPeriods(): Promise<{ created: number; skipped: number }> {
    const employees = await (this.prisma as any).dingtalkEmployee.findMany({
      where: { joinDate: { not: null } },
      select: { userId: true, joinDate: true },
    });
    const repo = (this.prisma as any).dingtalkEmployeeEmploymentPeriod;
    let created = 0, skipped = 0;
    for (const emp of employees) {
      const existing = await repo.findFirst({ where: { userId: emp.userId, periodIndex: 1 } });
      if (existing) { skipped++; continue; }
      await repo.create({
        data: {
          userId: emp.userId,
          periodIndex: 1,
          joinDate: emp.joinDate,
          leaveDate: null,
          countInTenure: true,
          note: '自动初始化',
        },
      });
      await this.calculateTenureDays(emp.userId); // 初始化时也写回缓存
      created++;
    }
    return { created, skipped };
  }
}
