package com.xyw.console.asset.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xyw.console.asset.dto.CompanyPersonLookupResponse;
import com.xyw.console.asset.dto.CompanyProfileLookupResponse;
import com.xyw.console.asset.dto.PhoneAssetLookupResponse;
import com.xyw.console.asset.dto.WecomAccountPageQuery;
import com.xyw.console.asset.dto.WecomAccountPageResponse;
import com.xyw.console.asset.dto.WecomAccountResponse;
import com.xyw.console.asset.dto.WecomAccountSaveRequest;
import com.xyw.console.asset.entity.AssetDeviceEntity;
import com.xyw.console.asset.exception.PhoneAssetValidationException;
import com.xyw.console.asset.entity.CompanyPersonEntity;
import com.xyw.console.asset.entity.CompanyProfileEntity;
import com.xyw.console.asset.entity.PhoneAssetEntity;
import com.xyw.console.asset.entity.WecomAccountEntity;
import com.xyw.console.asset.mapper.AssetDeviceMapper;
import com.xyw.console.asset.mapper.CompanyPersonMapper;
import com.xyw.console.asset.mapper.CompanyProfileMapper;
import com.xyw.console.asset.mapper.PhoneAssetMapper;
import com.xyw.console.asset.mapper.WecomAccountMapper;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/** 文件用途（白话）：读取企微账号资产，并把页面看不懂的关联 ID 转成公司、手机号、设备和人员名称。 */
@Service
public class WecomAccountService {
  private final WecomAccountMapper wecomAccountMapper;
  private final CompanyProfileMapper companyProfileMapper;
  private final PhoneAssetMapper phoneAssetMapper;
  private final AssetDeviceMapper assetDeviceMapper;
  private final CompanyPersonMapper companyPersonMapper;

  /**
   * 代码作用（白话）：接收企微和关联资产表的数据库访问入口，供列表查询一次性解析名称。
   * 关联文件：WecomAccountMapper.java、CompanyProfileMapper.java、PhoneAssetMapper.java、AssetDeviceMapper.java、CompanyPersonMapper.java。
   * 关联逻辑（调用链/数据流）：Controller -> Service -> 五个 Mapper -> 资产表。
   */
  public WecomAccountService(
      WecomAccountMapper wecomAccountMapper,
      CompanyProfileMapper companyProfileMapper,
      PhoneAssetMapper phoneAssetMapper,
      AssetDeviceMapper assetDeviceMapper,
      CompanyPersonMapper companyPersonMapper) {
    this.wecomAccountMapper = wecomAccountMapper;
    this.companyProfileMapper = companyProfileMapper;
    this.phoneAssetMapper = phoneAssetMapper;
    this.assetDeviceMapper = assetDeviceMapper;
    this.companyPersonMapper = companyPersonMapper;
  }

  /**
   * 代码作用（白话）：按分页和筛选条件读取未删除企微资产，并补齐关联名称后返回页面数据。
   * 关联文件：WecomAccountController.java、WecomAccountPageQuery.java、WecomAccountResponse.java。
   * 关联逻辑（调用链/数据流）：GET 请求 -> page -> WecomAccountMapper + 关联 Mapper -> 分页 DTO -> Vue 表格。
   */
  /** 代码作用（白话）：创建企业微信资产并关联已有手机号或自动创建外部号码。关联文件：WecomAccountController.java、PhoneAssetEntity.java。关联逻辑（调用链/数据流）：POST 表单 -> create -> 手机号查询或新增 -> 企微插入。 */
  @Transactional
  public WecomAccountResponse create(WecomAccountSaveRequest request) {
    String phoneNumber = normalizePhoneNumber(request.phoneNumber());
    PhoneAssetEntity phone = phoneAssetMapper.selectOne(new LambdaQueryWrapper<PhoneAssetEntity>().eq(PhoneAssetEntity::getDeleteTime, 0L).eq(PhoneAssetEntity::getPhoneNumber, phoneNumber));
    boolean created = phone == null;
    LocalDateTime now = LocalDateTime.now();
    if (created) { phone = new PhoneAssetEntity(); phone.setPhoneNumber(phoneNumber); phone.setNumberType("EXTERNAL"); phone.setSourceAssetType("WECOM"); phone.setDisposalStatus("正常使用"); phone.setCreateTime(now); phone.setUpdateTime(now); phone.setDeleteTime(0L); phone.setLinkedWecomAccounts("[]"); phone.setLinkedWechatAccounts("[]"); phone.setLinkedDouyinAccounts("[]"); phone.setLinkedDomainAccounts("[]"); phone.setLinkedMerchants("[]"); if (phoneAssetMapper.insert(phone) != 1) throw new IllegalStateException("手机号资产新增失败"); }
    WecomAccountEntity entity = new WecomAccountEntity(); entity.setWecomName(request.wecomName()); entity.setWecomAlias(request.wecomAlias()); entity.setWecomAccount(request.wecomAccount()); entity.setCompanyProfileId(request.companyProfileId()); entity.setPhoneAssetId(phone.getId()); entity.setPhoneLinkMode(created ? "CREATED" : "EXISTING"); entity.setRealNameOwner(request.realNameOwner()); entity.setRealNameOwnerStatus(request.realNameOwnerStatus() == null ? "在职" : request.realNameOwnerStatus()); entity.setGender(request.gender()); entity.setOperatorPersonId(request.operatorPersonId()); entity.setCreateTime(now); entity.setUpdateTime(now); entity.setDeleteTime(0L); if (wecomAccountMapper.insert(entity) != 1) throw new IllegalStateException("企业微信资产新增失败"); if (created) { phone.setSourceAssetId(entity.getId()); phoneAssetMapper.updateById(phone); } return toResponse(entity, Map.of(), Map.of(phone.getId(), phone.getPhoneNumber()), Map.of(), Map.of());
  }

  /** Code purpose (plain language): finds active company profiles for the registration-subject selector. Related files: WecomAccountController.java, CompanyProfileEntity.java. Data flow: form keyword -> GET lookup -> mapper -> compact DTO. */
  public List<CompanyProfileLookupResponse> searchCompanyProfiles(String keyword) {
    LambdaQueryWrapper<CompanyProfileEntity> query = new LambdaQueryWrapper<CompanyProfileEntity>().eq(CompanyProfileEntity::getDeleteTime, 0L);
    if (hasText(keyword)) query.and(wrapper -> wrapper.like(CompanyProfileEntity::getCompanyName, keyword).or().like(CompanyProfileEntity::getShortName, keyword));
    return companyProfileMapper.selectList(query.orderByDesc(CompanyProfileEntity::getId)).stream().limit(20).map(item -> new CompanyProfileLookupResponse(item.getId(), item.getCompanyName(), item.getShortName())).toList();
  }

  /** Code purpose (plain language): finds active phone assets which may be reused as registration phones. Related files: WecomAccountController.java, PhoneAssetEntity.java. Data flow: form keyword -> GET lookup -> mapper -> compact DTO. */
  public List<PhoneAssetLookupResponse> searchPhoneAssets(String keyword) {
    LambdaQueryWrapper<PhoneAssetEntity> query = new LambdaQueryWrapper<PhoneAssetEntity>().eq(PhoneAssetEntity::getDeleteTime, 0L).like(hasText(keyword), PhoneAssetEntity::getPhoneNumber, keyword).orderByDesc(PhoneAssetEntity::getId);
    return phoneAssetMapper.selectList(query).stream().limit(20).map(item -> new PhoneAssetLookupResponse(item.getId(), item.getPhoneNumber(), item.getNumberType())).toList();
  }

  /** Code purpose (plain language): finds active company people for the optional WeCom owner selector. Related files: WecomAccountController.java, CompanyPersonEntity.java. Data flow: form keyword -> GET lookup -> mapper -> compact DTO. */
  public List<CompanyPersonLookupResponse> searchCompanyPersons(String keyword) {
    LambdaQueryWrapper<CompanyPersonEntity> query = new LambdaQueryWrapper<CompanyPersonEntity>().eq(CompanyPersonEntity::getDeleteTime, 0L).like(hasText(keyword), CompanyPersonEntity::getPersonName, keyword).orderByDesc(CompanyPersonEntity::getId);
    return companyPersonMapper.selectList(query).stream().limit(20).map(item -> new CompanyPersonLookupResponse(item.getId(), item.getPersonName())).toList();
  }

  /** Code purpose (plain language): removes the China country prefix and only accepts an eleven-digit mobile number before it is looked up or created. Related files: WecomAccountSaveRequest.java, PhoneAssetService.java. Data flow: create form -> normalization -> lookup/create. */
  private String normalizePhoneNumber(String value) {
    String normalized = value == null ? "" : value.trim();
    if (normalized.startsWith("+86")) normalized = normalized.substring(3);
    if (!normalized.matches("\\d{11}")) throw new PhoneAssetValidationException("手机号必须是 11 位数字");
    return normalized;
  }
  public WecomAccountPageResponse page(WecomAccountPageQuery query) {
    Page<WecomAccountEntity> page = wecomAccountMapper.selectPage(
        new Page<>(query.resolvedPage(), query.resolvedSize()), activeQuery(query));
    List<WecomAccountEntity> entities = page.getRecords();
    Map<Long, String> companyNames = companyNames(collectIds(entities, WecomAccountEntity::getCompanyProfileId));
    Map<Long, String> phoneNumbers = phoneNumbers(collectIds(entities, WecomAccountEntity::getPhoneAssetId));
    Map<Long, String> deviceNames = deviceNames(collectIds(entities, WecomAccountEntity::getDeviceId));
    Map<Long, String> operatorPersonNames = operatorPersonNames(collectIds(entities, WecomAccountEntity::getOperatorPersonId));
    List<WecomAccountResponse> records = entities.stream()
        .map(entity -> toResponse(entity, companyNames, phoneNumbers, deviceNames, operatorPersonNames))
        .toList();
    return new WecomAccountPageResponse(records, page.getTotal(), query.resolvedPage(), query.resolvedSize());
  }

  /**
   * 代码作用（白话）：构造只含正常企微记录的筛选条件，并在有输入时附加名称和账号筛选。
   * 关联文件：WecomAccountEntity.java、WecomAccountPageQuery.java、WecomAccountMapper.java。
   * 关联逻辑（调用链/数据流）：查询参数 -> LambdaQueryWrapper -> as_wecom_account SQL -> 分页结果。
   */
  /** 代码作用（白话）：组合统一关键词、注册主体、实名状态和兼容筛选条件，只返回未删除的企业微信资产。关联文件：WecomAccountPageQuery.java、PhoneAssetEntity.java。关联逻辑（调用链/数据流）：顶部条件 -> 手机号 ID 匹配（如有）-> 企业微信 SQL 条件 -> 分页结果。 */
  private LambdaQueryWrapper<WecomAccountEntity> activeQuery(WecomAccountPageQuery query) {
    LambdaQueryWrapper<WecomAccountEntity> wrapper = new LambdaQueryWrapper<WecomAccountEntity>()
        .eq(WecomAccountEntity::getDeleteTime, 0L)
        .eq(hasText(query.wecomAccount()), WecomAccountEntity::getWecomAccount, query.wecomAccount())
        .eq(query.phoneAssetId() != null, WecomAccountEntity::getPhoneAssetId, query.phoneAssetId())
        .eq(query.companyProfileId() != null, WecomAccountEntity::getCompanyProfileId, query.companyProfileId())
        .eq(hasText(query.realNameOwnerStatus()), WecomAccountEntity::getRealNameOwnerStatus, query.realNameOwnerStatus());
    if (hasText(query.keyword())) {
      Set<Long> phoneIds = matchingPhoneAssetIds(query.keyword());
      wrapper.and(nested -> nested.like(WecomAccountEntity::getWecomName, query.keyword())
          .or(!phoneIds.isEmpty(), byPhone -> byPhone.in(WecomAccountEntity::getPhoneAssetId, phoneIds)));
    }
    return wrapper.orderByDesc(WecomAccountEntity::getId);
  }

  /** 代码作用（白话）：找出手机号包含统一搜索词的有效手机号资产 ID。关联文件：PhoneAssetEntity.java、WecomAccountPageQuery.java。关联逻辑（调用链/数据流）：统一搜索词 -> 手机号模糊查询 -> 企业微信关联手机号条件。 */
  private Set<Long> matchingPhoneAssetIds(String keyword) {
    return phoneAssetMapper.selectList(new LambdaQueryWrapper<PhoneAssetEntity>()
        .eq(PhoneAssetEntity::getDeleteTime, 0L)
        .like(PhoneAssetEntity::getPhoneNumber, keyword)).stream()
        .map(PhoneAssetEntity::getId)
        .filter(id -> id != null)
        .collect(Collectors.toSet());
  }
  /**
   * 代码作用（白话）：判断筛选文本是否有实际内容，避免空字符串参与精确查询而把正常结果筛掉。
   * 关联文件：WecomAccountPageQuery.java、WecomAccountService.java。
   * 关联逻辑（调用链/数据流）：URL 筛选参数 -> hasText -> activeQuery 是否追加条件。
   */
  private boolean hasText(String value) {
    return value != null && !value.isBlank();
  }

  /**
   * 代码作用（白话）：从当前页企微记录中收集某一种关联 ID 并去重，供后续一次性查询名称。
   * 关联文件：WecomAccountEntity.java、WecomAccountService.java。
   * 关联逻辑（调用链/数据流）：当前页 records -> ID 集合 -> 关联 Mapper IN 查询。
   */
  private Set<Long> collectIds(List<WecomAccountEntity> entities, Function<WecomAccountEntity, Long> idGetter) {
    return entities.stream().map(idGetter).filter(id -> id != null).collect(Collectors.toSet());
  }

  /**
   * 代码作用（白话）：批量读取公司档案名称，供公司档案 ID 在页面显示为名称（ID）。
   * 关联文件：CompanyProfileEntity.java、CompanyProfileMapper.java、WecomAccountResponse.java。
   * 关联逻辑（调用链/数据流）：companyProfileId 集合 -> as_company_profile -> companyProfileName。
   */
  private Map<Long, String> companyNames(Set<Long> ids) {
    if (ids.isEmpty()) {
      return Map.of();
    }
    return toNameMap(companyProfileMapper.selectList(new LambdaQueryWrapper<CompanyProfileEntity>()
        .in(CompanyProfileEntity::getId, ids).eq(CompanyProfileEntity::getDeleteTime, 0L)),
        CompanyProfileEntity::getId, CompanyProfileEntity::getCompanyName);
  }

  /**
   * 代码作用（白话）：批量读取手机号资产号码，供手机号资产 ID 在页面显示为手机号（ID）。
   * 关联文件：PhoneAssetEntity.java、PhoneAssetMapper.java、WecomAccountResponse.java。
   * 关联逻辑（调用链/数据流）：phoneAssetId 集合 -> as_phone_asset -> phoneNumber。
   */
  private Map<Long, String> phoneNumbers(Set<Long> ids) {
    if (ids.isEmpty()) {
      return Map.of();
    }
    return toNameMap(phoneAssetMapper.selectList(new LambdaQueryWrapper<PhoneAssetEntity>()
        .in(PhoneAssetEntity::getId, ids).eq(PhoneAssetEntity::getDeleteTime, 0L)),
        PhoneAssetEntity::getId, PhoneAssetEntity::getPhoneNumber);
  }

  /**
   * 代码作用（白话）：批量读取设备名称，供设备 ID 在页面显示为设备名称（ID）。
   * 关联文件：AssetDeviceEntity.java、AssetDeviceMapper.java、WecomAccountResponse.java。
   * 关联逻辑（调用链/数据流）：deviceId 集合 -> as_asset_device -> deviceName。
   */
  private Map<Long, String> deviceNames(Set<Long> ids) {
    if (ids.isEmpty()) {
      return Map.of();
    }
    return toNameMap(assetDeviceMapper.selectList(new LambdaQueryWrapper<AssetDeviceEntity>()
        .in(AssetDeviceEntity::getId, ids).eq(AssetDeviceEntity::getDeleteTime, 0L)),
        AssetDeviceEntity::getId, AssetDeviceEntity::getDeviceName);
  }

  /**
   * 代码作用（白话）：批量读取经办人员名称，供经办人 ID 在页面显示为人员名称（ID）。
   * 关联文件：CompanyPersonEntity.java、CompanyPersonMapper.java、WecomAccountResponse.java。
   * 关联逻辑（调用链/数据流）：operatorPersonId 集合 -> as_company_person -> personName。
   */
  private Map<Long, String> operatorPersonNames(Set<Long> ids) {
    if (ids.isEmpty()) {
      return Map.of();
    }
    return toNameMap(companyPersonMapper.selectList(new LambdaQueryWrapper<CompanyPersonEntity>()
        .in(CompanyPersonEntity::getId, ids).eq(CompanyPersonEntity::getDeleteTime, 0L)),
        CompanyPersonEntity::getId, CompanyPersonEntity::getPersonName);
  }

  /**
   * 代码作用（白话）：把一批“主键和名称”实体整理成查找表，让每条企微记录不用再次查询数据库。
   * 关联文件：CompanyProfileEntity.java、PhoneAssetEntity.java、AssetDeviceEntity.java、CompanyPersonEntity.java。
   * 关联逻辑（调用链/数据流）：关联 Mapper 结果 -> ID/名称 Map -> toResponse。
   */
  private <T> Map<Long, String> toNameMap(Collection<T> entities, Function<T, Long> idGetter, Function<T, String> nameGetter) {
    return entities.stream().collect(Collectors.toMap(idGetter, nameGetter));
  }

  /**
   * 代码作用（白话）：把企微数据库实体和已解析的关联名称组合成页面可直接使用的一行数据。
   * 关联文件：WecomAccountEntity.java、WecomAccountResponse.java、WecomAccountView.js。
   * 关联逻辑（调用链/数据流）：实体 + 名称 Map -> Response -> ApiResponse -> Element Plus 表格。
   */
  /** Code purpose (plain language): safely reads an optional relation name without passing a null key to immutable empty maps. Related files: WecomAccountResponse.java. Data flow: optional relation ID -> safe map lookup -> response. */
  private String valueOf(Map<Long, String> names, Long id) { return id == null ? null : names.get(id); }

  private WecomAccountResponse toResponse(
      WecomAccountEntity entity,
      Map<Long, String> companyNames,
      Map<Long, String> phoneNumbers,
      Map<Long, String> deviceNames,
      Map<Long, String> operatorPersonNames) {
    return new WecomAccountResponse(
        entity.getId(), entity.getWecomName(), entity.getWecomAlias(), entity.getWecomAccount(),
        entity.getCompanyProfileId(), valueOf(companyNames, entity.getCompanyProfileId()),
        entity.getPhoneAssetId(), valueOf(phoneNumbers, entity.getPhoneAssetId()), entity.getPhoneLinkMode(),
        entity.getRealNameOwner(), entity.getRealNameOwnerStatus(), entity.getGender(),
        entity.getDeviceId(), valueOf(deviceNames, entity.getDeviceId()),
        entity.getOperatorPersonId(), valueOf(operatorPersonNames, entity.getOperatorPersonId()),
        entity.getCreateTime(), entity.getUpdateTime());
  }
}
