import { Injectable } from '@nestjs/common';
import { DingtalkYidaService } from '../sdk/dingtalk-yida.service';
import { DingtalkHrmService } from '../sdk/dingtalk-hrm.service';
import {
  FORM_UUIDS,
  EMPLOYEE_INFO_FIELDS,
  OLD_EMPLOYEE_INFO_FIELDS,
  EMPLOYEE_FIELD_MAPPING,
} from '../constants';
import { SyncExecutionResult } from './business-trip-sync.service';
import { SyncLogger } from './sync-logger';

@Injectable()
export class EmployeeInfoSyncService {
  private readonly oldFieldMapping = [
    { dingtalk: '工号', yida: OLD_EMPLOYEE_INFO_FIELDS.NUMBER },
    { dingtalk: '姓名', yida: OLD_EMPLOYEE_INFO_FIELDS.NAME },
    { dingtalk: '国家', yida: OLD_EMPLOYEE_INFO_FIELDS.COUNTRY },
    { dingtalk: '办公地点', yida: OLD_EMPLOYEE_INFO_FIELDS.WORK_PLACE },
    { dingtalk: '合同公司', yida: OLD_EMPLOYEE_INFO_FIELDS.CONTRACT_COMPANY },
    { dingtalk: '开户行', yida: OLD_EMPLOYEE_INFO_FIELDS.BANK },
    { dingtalk: '银行卡号后四位', yida: OLD_EMPLOYEE_INFO_FIELDS.BANK_NO },
    { dingtalk: '职位', yida: OLD_EMPLOYEE_INFO_FIELDS.TITLE },
    { dingtalk: '岗位职级', yida: OLD_EMPLOYEE_INFO_FIELDS.RANK },
    { dingtalk: '入职时间', yida: OLD_EMPLOYEE_INFO_FIELDS.START_DATE },
    { dingtalk: '手机号', yida: OLD_EMPLOYEE_INFO_FIELDS.PHONE },
  ];

  constructor(
    private yidaService: DingtalkYidaService,
    private hrmService: DingtalkHrmService,
  ) {}

  /**
   * 更新员工信息表（每周更新 - 新表）
   * 对应Python: update_employee_info()
   */
  async syncNewTable(userId?: string): Promise<SyncExecutionResult> {
    const logger = new SyncLogger('EmployeeInfoSyncService');
    const startTime = Date.now();
    let count = 0;
    const errors: string[] = [];

    logger.log('------开始更新员工信息表（每周更新）------');
    if (userId) logger.log(`指定员工: ${userId}`);

    try {
      // 1. 获取部门全称
      logger.log('正在获取部门全称...');
      const departmentDict = await this.hrmService.getDepartmentFullNames();
      logger.log(`获取到 ${Object.keys(departmentDict).length} 个部门`);

      // 2. 获取在职员工信息
      logger.log('正在从钉钉HR获取在职员工...');
      const employeeIds = await this.hrmService.getOnJobEmployeeIds();
      logger.log(`钉钉在职员工: ${employeeIds.length} 人`);
      const employeeInDingtalk = await this.hrmService.getEmployeeInfoByIds(employeeIds);
      logger.log(`获取到 ${Object.keys(employeeInDingtalk).length} 人的详细信息`);

      // 3. 获取宜搭员工信息表数据
      logger.log('正在获取宜搭员工信息表...');
      const searchCondition = JSON.stringify([
        {
          key: EMPLOYEE_INFO_FIELDS.STATUS,
          value: '正常',
          type: 'TEXT',
          operator: 'eq',
          componentName: 'TextField',
        },
      ]);

      const yidaEmployeeList = await this.yidaService.searchForm(
        FORM_UUIDS.EMPLOYEE_INFO_WEEKLY,
        searchCondition,
      );
      logger.log(`宜搭现有在职员工: ${yidaEmployeeList.length} 人`);

      const employeeInYida: Record<string, any> = {};
      for (const e of yidaEmployeeList) {
        employeeInYida[e.formData[EMPLOYEE_INFO_FIELDS.USER_ID]] = e.formData;
      }

      // 4. 对比数据
      let dingtalkKeys = new Set(Object.keys(employeeInDingtalk));
      let yidaKeys = new Set(Object.keys(employeeInYida));
      const onlyInDingtalk = [...dingtalkKeys].filter(k => !yidaKeys.has(k));
      const onlyInYida = [...yidaKeys].filter(k => !dingtalkKeys.has(k));
      const inBoth = [...dingtalkKeys].filter(k => yidaKeys.has(k));
      logger.log(`对比结果: 新增 ${onlyInDingtalk.length} 人, 离职 ${onlyInYida.length} 人, 在职更新 ${inBoth.length} 人`);

      if (userId) {
        dingtalkKeys = new Set([userId].filter(k => dingtalkKeys.has(k)));
        yidaKeys = new Set([userId].filter(k => yidaKeys.has(k)));
      }

      // 只在钉钉中的员工 -> 新增到宜搭
      for (const userId of dingtalkKeys) {
        if (yidaKeys.has(userId)) continue;

        try {
          const employeeData = this.parseEmployeeData(employeeInDingtalk[userId], departmentDict);
          const formDataJson = this.buildFormDataJson(userId, employeeData);

          const searchCond = JSON.stringify([
            { key: EMPLOYEE_INFO_FIELDS.USER_ID, value: userId, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
          ]);

          await this.yidaService.createOrUpdateForm(
            FORM_UUIDS.EMPLOYEE_INFO_WEEKLY,
            searchCond,
            formDataJson,
          );
          count++;
          logger.log(`新表新增: ${employeeData['姓名'] || userId} (${userId})`);
        } catch (error: any) {
          errors.push(`新增员工${userId}失败: ${error.message}`);
        }
      }

      // 只在宜搭中的员工 -> 标记为已离职
      for (const userId of yidaKeys) {
        if (dingtalkKeys.has(userId)) continue;

        try {
          const searchCond = JSON.stringify([
            { key: EMPLOYEE_INFO_FIELDS.USER_ID, value: userId, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
          ]);
          const formDataJson = JSON.stringify({ [EMPLOYEE_INFO_FIELDS.STATUS]: '已离职' });

          await this.yidaService.createOrUpdateForm(
            FORM_UUIDS.EMPLOYEE_INFO_WEEKLY,
            searchCond,
            formDataJson,
          );
          count++;
          logger.log(`新表标记离职: ${employeeInYida[userId]?.[EMPLOYEE_INFO_FIELDS.NAME] || userId} (${userId})`);
        } catch (error: any) {
          errors.push(`标记离职${userId}失败: ${error.message}`);
        }
      }

      // 两边都有的员工 -> 检查更新
      for (const userId of dingtalkKeys) {
        if (!yidaKeys.has(userId)) continue;

        try {
          const employeeData = this.parseEmployeeData(employeeInDingtalk[userId], departmentDict);
          const yidaData = employeeInYida[userId];
          const changes = this.getNewRecordChanges(employeeData, yidaData);

          if (changes.length > 0) {
            const formDataJson = this.buildFormDataJson(userId, employeeData);
            const searchCond = JSON.stringify([
              { key: EMPLOYEE_INFO_FIELDS.USER_ID, value: userId, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
            ]);

            await this.yidaService.createOrUpdateForm(
              FORM_UUIDS.EMPLOYEE_INFO_WEEKLY,
              searchCond,
              formDataJson,
            );
            count++;
            logger.log(`新表更新: ${employeeData['姓名'] || userId} (${userId}); 变更: ${changes.join(', ')}`);
          }
        } catch (error: any) {
          errors.push(`更新员工${userId}失败: ${error.message}`);
        }
      }
    } 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(),
    };
  }

  /**
   * 更新员工信息表（旧表）
   * 对应Python: update_form_employee_info()
   */
  async syncOldTable(userId?: string): Promise<SyncExecutionResult> {
    const logger = new SyncLogger('EmployeeInfoSyncService');
    const startTime = Date.now();
    let count = 0;
    const errors: string[] = [];

    logger.log('----开始更新员工信息表（旧）----');
    if (userId) logger.log(`指定员工: ${userId}`);

    try {
      logger.log('正在获取部门信息...');
      const departParent = await this.hrmService.getDepartmentParents();
      logger.log('正在从钉钉HR获取在职员工...');
      const employeeIds = await this.hrmService.getOnJobEmployeeIds();
      logger.log(`钉钉在职员工: ${employeeIds.length} 人`);
      const employeeDingtalk = await this.hrmService.getEmployeeInfoByIds(employeeIds);

      logger.log('正在获取宜搭旧表员工数据...');
      const searchCondition = JSON.stringify([
        {
          key: OLD_EMPLOYEE_INFO_FIELDS.STATUS,
          value: '正常',
          type: 'TEXT',
          operator: 'eq',
          componentName: 'TextField',
        },
      ]);

      const yidaList = await this.yidaService.searchForm(
        FORM_UUIDS.OLD_EMPLOYEE_INFO,
        searchCondition,
      );
      logger.log(`宜搭旧表在职员工: ${yidaList.length} 人`);

      const employeeYida: Record<string, any> = {};
      for (const e of yidaList) {
        const userId = e.formData[OLD_EMPLOYEE_INFO_FIELDS.USER_ID];
        const department = e.formData[OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT] || '';
        if (!employeeYida[userId]) {
          employeeYida[userId] = {
            ...e.formData,
            [OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT]: new Set<string>(department ? [department] : []),
          };
          continue;
        }
        if (department) {
          (employeeYida[userId][OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT] as Set<string>).add(department);
        }
      }

      let dingtalkKeys = new Set(Object.keys(employeeDingtalk));
      let yidaKeys = new Set(Object.keys(employeeYida));
      const onlyInYida = [...yidaKeys].filter(k => !dingtalkKeys.has(k));
      const onlyInDingtalk = [...dingtalkKeys].filter(k => !yidaKeys.has(k));
      const inBoth = [...dingtalkKeys].filter(k => yidaKeys.has(k));
      logger.log(`对比结果: 新增 ${onlyInDingtalk.length} 人, 离职 ${onlyInYida.length} 人, 在职更新 ${inBoth.length} 人`);

      if (userId) {
        dingtalkKeys = new Set([userId].filter(k => dingtalkKeys.has(k)));
        yidaKeys = new Set([userId].filter(k => yidaKeys.has(k)));
      }

      // 只在宜搭中 -> 标记离职（一个 userId 可能有多条记录，需逐条更新）
      for (const userId of yidaKeys) {
        if (dingtalkKeys.has(userId)) continue;

        try {
          const departments = employeeYida[userId]?.[OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT] as Set<string> | undefined;
          if (departments && departments.size > 0) {
            for (const dept of departments) {
              const searchCond = JSON.stringify([
                { key: OLD_EMPLOYEE_INFO_FIELDS.USER_ID, value: userId, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
                { key: OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT, value: dept, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
              ]);
              await this.yidaService.createOrUpdateForm(
                FORM_UUIDS.OLD_EMPLOYEE_INFO,
                searchCond,
                JSON.stringify({ [OLD_EMPLOYEE_INFO_FIELDS.STATUS]: '已离职' }),
              );
              count++;
              logger.log(`旧表标记离职: ${employeeYida[userId]?.[OLD_EMPLOYEE_INFO_FIELDS.NAME] || userId} (${userId}) -> ${dept}`);
            }
          } else {
            // 没有部门信息时，用 userId 兜底
            const searchCond = JSON.stringify([
              { key: OLD_EMPLOYEE_INFO_FIELDS.USER_ID, value: userId, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
            ]);
            await this.yidaService.createOrUpdateForm(
              FORM_UUIDS.OLD_EMPLOYEE_INFO,
              searchCond,
              JSON.stringify({ [OLD_EMPLOYEE_INFO_FIELDS.STATUS]: '已离职' }),
            );
            count++;
            logger.log(`旧表标记离职: ${employeeYida[userId]?.[OLD_EMPLOYEE_INFO_FIELDS.NAME] || userId} (${userId})`);
          }
        } catch (error: any) {
          errors.push(`标记离职${userId}失败: ${error.message}`);
        }
      }

      // 只在钉钉中 -> 新增
      for (const userId of dingtalkKeys) {
        if (yidaKeys.has(userId)) continue;

        try {
          const employeeData = this.parseEmployeeDataOld(employeeDingtalk[userId], departParent);
          const departments = employeeData['__departments'] as Set<string>;

          for (const dept of departments) {
            const searchCond = JSON.stringify([
              { key: OLD_EMPLOYEE_INFO_FIELDS.USER_ID, value: userId, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
              { key: OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT, value: dept, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
            ]);

            await this.yidaService.createOrUpdateForm(
              FORM_UUIDS.OLD_EMPLOYEE_INFO,
              searchCond,
              JSON.stringify(this.buildOldFormData(userId, dept, employeeData)),
            );
            count++;
            logger.log(`旧表新增: ${employeeData['姓名'] || userId} (${userId}) -> ${dept}`);
          }
        } catch (error: any) {
          errors.push(`更新员工${userId}旧表失败: ${error.message}`);
        }
      }

      // 两边都有 -> 有变化才更新
      for (const userId of dingtalkKeys) {
        if (!yidaKeys.has(userId)) continue;

        try {
          const employeeData = this.parseEmployeeDataOld(employeeDingtalk[userId], departParent);
          const yidaData = employeeYida[userId];
          const departments = employeeData['__departments'] as Set<string>;
          const changes = this.getOldRecordChanges(employeeData, departments, yidaData);

          if (changes.length === 0) {
            continue;
          }

          for (const dept of departments) {
            const searchCond = JSON.stringify([
              { key: OLD_EMPLOYEE_INFO_FIELDS.USER_ID, value: userId, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
              { key: OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT, value: dept, type: 'TEXT', operator: 'eq', componentName: 'TextField' },
            ]);

            await this.yidaService.createOrUpdateForm(
              FORM_UUIDS.OLD_EMPLOYEE_INFO,
              searchCond,
              JSON.stringify(this.buildOldFormData(userId, dept, employeeData)),
            );
            count++;
            logger.log(`旧表更新: ${employeeData['姓名'] || userId} (${userId}) -> ${dept}; 变更: ${changes.join(', ')}`);
          }
        } catch (error: any) {
          errors.push(`更新员工${userId}旧表失败: ${error.message}`);
        }
      }
    } 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(),
    };
  }

  /** 解析钉钉员工数据为字段字典（新表） */
  private parseEmployeeData(
    employee: any,
    departmentDict: Record<number, { name: string; firstLevel: string }>,
  ): Record<string, string> {
    const result: Record<string, string> = {};
    const fieldDataList = employee.field_data_list || [];

    for (const data of fieldDataList) {
      if (data.field_name === '部门id') {
        const deptIds = (data.field_value_list?.[0]?.value || '').split('|').filter(Boolean);
        const deptNames = deptIds
          .map((id: string) => departmentDict[parseInt(id)]?.name || '')
          .filter(Boolean)
          .join('#');
        result['部门'] = deptNames;
      } else {
        result[data.field_name] = data.field_value_list?.[0]?.value || '';
      }
    }

    return result;
  }

  /** 解析钉钉员工数据为字段字典（旧表） */
  private parseEmployeeDataOld(
    employee: any,
    departParent: Record<number, { name: string; firstLevel: string }>,
  ): Record<string, any> {
    const result: Record<string, any> = {};
    const fieldDataList = employee.field_data_list || [];
    const departments = new Set<string>();

    for (const data of fieldDataList) {
      if (data.field_name === '部门id') {
        const deptIds = (data.field_value_list?.[0]?.value || '').split('|').filter(Boolean);
        for (const deptId of deptIds) {
          const parent = departParent[parseInt(deptId)];
          if (parent && parent.firstLevel !== '待定用户') {
            departments.add(parent.firstLevel);
          }
        }
      } else {
        result[data.field_name] = data.field_value_list?.[0]?.value || '';
      }
    }

    result['__departments'] = departments;
    return result;
  }

  /** 构建表单数据JSON（新表） */
  private buildFormDataJson(userId: string, employeeData: Record<string, string>): string {
    const formData: Record<string, string> = {};
    formData[EMPLOYEE_INFO_FIELDS.USER_ID] = userId;

    for (const mapping of EMPLOYEE_FIELD_MAPPING) {
      formData[mapping.yidaField] = employeeData[mapping.dingtalkField] || ' ';
    }

    formData[EMPLOYEE_INFO_FIELDS.STATUS] = '正常';
    return JSON.stringify(formData);
  }

  private getNewRecordChanges(employeeData: Record<string, string>, yidaData: Record<string, any>): string[] {
    const changes: string[] = [];

    for (const mapping of EMPLOYEE_FIELD_MAPPING) {
      const previous = yidaData[mapping.yidaField] || '';
      const current = employeeData[mapping.dingtalkField] || '';
      if (previous !== current) {
        changes.push(`${mapping.dingtalkField}:${previous || '(空)'}->${current || '(空)'}`);
      }
    }

    return changes;
  }

  private buildOldFormData(userId: string, department: string, employeeData: Record<string, any>): Record<string, string> {
    const formData: Record<string, string> = {};
    formData[OLD_EMPLOYEE_INFO_FIELDS.USER_ID] = userId;
    formData[OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT] = department;
    formData[OLD_EMPLOYEE_INFO_FIELDS.STATUS] = '正常';

    for (const mapping of this.oldFieldMapping) {
      formData[mapping.yida] = employeeData[mapping.dingtalk] || ' ';
    }

    return formData;
  }

  private getOldRecordChanges(
    employeeData: Record<string, any>,
    departments: Set<string>,
    yidaData: Record<string, any>,
  ): string[] {
    const changes: string[] = [];
    const yidaDepartments = yidaData[OLD_EMPLOYEE_INFO_FIELDS.DEPARTMENT] as Set<string> | undefined;
    if (!this.isSameStringSet(departments, yidaDepartments || new Set<string>())) {
      const current = [...departments].sort().join('|') || '(空)';
      const previous = [...(yidaDepartments || new Set<string>())].sort().join('|') || '(空)';
      changes.push(`部门:${previous}->${current}`);
    }

    for (const mapping of this.oldFieldMapping) {
      if ((yidaData[mapping.yida] || '') !== (employeeData[mapping.dingtalk] || '')) {
        changes.push(`${mapping.dingtalk}:${yidaData[mapping.yida] || '(空)'}->${employeeData[mapping.dingtalk] || '(空)'}`);
      }
    }

    return changes;
  }

  private isSameStringSet(left: Set<string>, right: Set<string>): boolean {
    if (left.size !== right.size) return false;
    for (const value of left) {
      if (!right.has(value)) return false;
    }
    return true;
  }
}
