package com.red.circle.order.app.command.pay.web;

import com.red.circle.common.business.core.util.CountryCodeAliasUtils;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountry;
import com.red.circle.order.infra.database.rds.service.pay.PayCountryChannelDetailsService;
import com.red.circle.order.infra.database.rds.service.pay.PayCountryService;
import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient;
import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * 支付国家兼容映射.
 */
@Component
@RequiredArgsConstructor
public class PayCountryAliasResolver {

  private final PayCountryService payCountryService;
  private final SysCountryCodeClient sysCountryCodeClient;
  private final PayCountryChannelDetailsService payCountryChannelDetailsService;

  public PayCountry resolveUsablePayCountry(Long payCountryOrCountryId) {
    if (Objects.isNull(payCountryOrCountryId)) {
      return payCountryService.getShelfSortMax();
    }

    PayCountry payCountry = payCountryService.getById(payCountryOrCountryId);
    if (isUsablePayCountry(payCountry)) {
      return payCountry;
    }

    Long countryId = Objects.nonNull(payCountry) ? payCountry.getCountryId() : payCountryOrCountryId;
    PayCountry compatiblePayCountry = resolveUsablePayCountryByCountryId(countryId);
    return Objects.nonNull(compatiblePayCountry) ? compatiblePayCountry : payCountry;
  }

  public PayCountry resolveUsablePayCountryByCountryId(Long countryId) {
    PayCountry payCountry = getUsablePayCountryByCountryId(countryId);
    if (Objects.nonNull(payCountry)) {
      return payCountry;
    }
    return getMappedUsablePayCountryByCountryId(countryId);
  }

  private PayCountry getMappedUsablePayCountryByCountryId(Long countryId) {
    SysCountryCodeDTO countryCode = getCountryCodeById(countryId);
    if (Objects.isNull(countryCode)) {
      return null;
    }

    for (String candidateCode : getMappedCountryCodes(countryCode)) {
      SysCountryCodeDTO mappedCountry = ResponseAssert.requiredSuccess(
          sysCountryCodeClient.getByCode(candidateCode));
      if (Objects.isNull(mappedCountry)
          || Objects.equals(mappedCountry.getId(), countryCode.getId())) {
        continue;
      }

      PayCountry mappedPayCountry = getUsablePayCountryByCountryId(mappedCountry.getId());
      if (Objects.nonNull(mappedPayCountry)) {
        return mappedPayCountry;
      }
    }
    return null;
  }

  private Set<String> getMappedCountryCodes(SysCountryCodeDTO countryCode) {
    LinkedHashSet<String> result = new LinkedHashSet<>();
    // 后续新增同国别名时，可在别名国家的 countryCodeAliases 配置真实国家码.
    if (CollectionUtils.isNotEmpty(countryCode.getCountryCodeAliases())) {
      countryCode.getCountryCodeAliases().stream()
          .map(CountryCodeAliasUtils::normalizePaymentCountryCode)
          .filter(StringUtils::isNotBlank)
          .forEach(result::add);
    }

    String defaultCode = CountryCodeAliasUtils.normalizePaymentCountryCode(
        StringUtils.isNotBlank(countryCode.getAlphaTwo())
            ? countryCode.getAlphaTwo()
            : countryCode.getAlphaThree());
    if (StringUtils.isNotBlank(defaultCode)) {
      result.add(defaultCode);
    }
    return result;
  }

  private SysCountryCodeDTO getCountryCodeById(Long countryId) {
    if (Objects.isNull(countryId)) {
      return null;
    }
    return ResponseAssert.requiredSuccess(sysCountryCodeClient.getById(countryId));
  }

  private PayCountry getUsablePayCountryByCountryId(Long countryId) {
    if (Objects.isNull(countryId)) {
      return null;
    }

    return payCountryService.query()
        .eq(PayCountry::getShelf, Boolean.TRUE)
        .eq(PayCountry::getCountryId, countryId)
        .orderByDesc(PayCountry::getSort)
        .list()
        .stream()
        .filter(this::isUsablePayCountry)
        .findFirst()
        .orElse(null);
  }

  private boolean isUsablePayCountry(PayCountry payCountry) {
    return Objects.nonNull(payCountry)
        && StringUtils.isNotBlank(payCountry.getCurrency())
        && CollectionUtils.isNotEmpty(
            payCountryChannelDetailsService.listChannelCode(payCountry.getId()));
  }
}
