package com.red.circle.console.app.service.app.team;

import com.alibaba.excel.EasyExcel;
import com.red.circle.console.app.convertor.team.TeamAppConvertor;
import com.red.circle.console.app.dto.clienobject.team.TeamBillCycleCO;
import com.red.circle.console.app.excel.model.TeamBillAnchorStatisticsModel;
import com.red.circle.console.infra.database.rds.entity.admin.User;
import com.red.circle.console.infra.database.rds.service.admin.UserService;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.inner.endpoint.team.agency.AgentTeamClient;
import com.red.circle.other.inner.endpoint.team.target.TeamBillCycleClient;
import com.red.circle.other.inner.model.cmd.team.AddTeamRemarkCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillByConditionQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillCycleBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillExportCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillRemarkCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillRemarkIdCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillStatusChangeCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCalculatorWorkCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamBillAnchorStatisticsModelDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCalculatorWorkResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamRemark;
import com.red.circle.other.inner.model.dto.agency.agency.TeamRemarkDTO;
import com.red.circle.other.inner.enums.team.TeamBillCycleStatus;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;

/**
 * 团队账单服务 实现.
 *
 * @author pengliang on 2022/12/13
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class TeamBillBackServiceImpl implements TeamBillBackService {

  private final UserService userService;
  private final AgentTeamClient agentTeamClient;
  private final TeamAppConvertor teamAppConvertor;
  private final UserProfileClient userProfileClient;
  private final TeamBillCycleClient billCycleClient;
  private final RegionConfigClient regionConfigClient;

  @Override
  public List<TeamBillCycleCO> listBillTable(TeamBillCycleBackQryCmd query) {

    List<TeamBillCycleCO> teamBillCycles = teamAppConvertor.toListTeamBillCycleCO(
        ResponseAssert.requiredSuccess(billCycleClient.listBillTable(query)));
    Map<Long, TeamProfileDTO> teamProfileMap = ResponseAssert.requiredSuccess(
        agentTeamClient.mapProfileByProfileIds(
            teamBillCycles.stream().map(TeamBillCycleCO::getTeamId).collect(Collectors.toSet())));

    Set<String> regionIds = getRegionIds(teamBillCycles);

    Map<Long, UserProfileDTO> userProfileMap = ResponseAssert.requiredSuccess(
        userProfileClient.mapByUserIds(
            Stream.of(
                    teamProfileMap.values().stream().map(TeamProfileDTO::getOwnUserId)
                        .collect(Collectors.toList()))
                .filter(CollectionUtils::isNotEmpty)
                .flatMap(Collection::stream)
                .collect(Collectors.toSet())
        ));

    Map<String, String> regionNameMap = ResponseAssert.requiredSuccess(
        regionConfigClient.mapRegionNameByRegionIds(regionIds));

    Map<Long, String> backUserMap = userService.mapBackUserNickname(
        Stream.of(
            teamBillCycles.stream()
                .map(TeamBillCycleCO::getUpdateUser)
                .filter(Objects::nonNull)
                .collect(Collectors.toList()),
            teamBillCycles.stream().map(TeamBillCycleCO::getInternalRemarks)
                .filter(CollectionUtils::isNotEmpty)
                .flatMap(Collection::stream)
                .map(TeamRemarkDTO::getCreateBackUser)
                .filter(Objects::nonNull)
                .collect(Collectors.toList())
        ).flatMap(Collection::stream).collect(Collectors.toSet()));
    return teamBillCycles.stream().map(teamBillCycle -> {

      TeamProfileDTO teamProfile = teamProfileMap.get(teamBillCycle.getTeamId());
      teamBillCycle.setTeamProfile(teamProfile);
      if (Objects.nonNull(teamProfile)) {
        teamBillCycle.setOwnUserProfile(userProfileMap.get(teamProfile.getOwnUserId()));
      }

      teamBillCycle.setUpdateUserNickname(backUserMap.get(teamBillCycle.getUpdateUser()));
      teamBillCycle.setRegionName(regionNameMap.get(teamBillCycle.getRegion()));

      if (CollectionUtils.isNotEmpty(teamBillCycle.getInternalRemarks())) {
        teamBillCycle.getInternalRemarks().forEach(
            remark -> remark.setCreateBackUserName(backUserMap.get(remark.getCreateBackUser())));
      }
      return teamBillCycle;
    }).collect(Collectors.toList());
  }


  private Set<String> getRegionIds(List<TeamBillCycleCO> teamBillCycles) {
    return teamBillCycles.stream().map(TeamBillCycleCO::getRegion).collect(Collectors.toSet());
  }

  @Override
  public TeamRemarkDTO addInternalRemarks(TeamBillRemarkCmd param) {
    TeamRemark teamRemark = new TeamRemark()
        .setRemarkId(IdWorkerUtils.getIdStr())
        .setRemark(param.getRemarks())
        .setCreateBackUser(param.getCreateUser())
        .setCreateTime(TimestampUtils.now());
    billCycleClient.addInternalRemarks(
        new AddTeamRemarkCmd()
            .setTeamId(param.getBillId())
            .setTeamRemark(teamRemark)
    );
    TeamRemarkDTO dto = teamAppConvertor.toTeamRemarkDTO(teamRemark);
    dto.setCreateBackUserName(Optional.ofNullable(userService.getById(param.getCreateUser()))
        .map(User::getNickname).orElse(""));
    return dto;
  }

  @Override
  public void delInternalRemarks(TeamBillRemarkIdCmd param) {
    billCycleClient.removeInternalRemarks(param.getBillId(), param.getRemarkId());
  }

  @Override
  public void updateRemarks(TeamBillRemarkCmd param) {
    billCycleClient.updateRemarks(param.getBillId(), param.getRemarks());
  }

  @Override
  public void updateBillStatus(TeamBillStatusChangeCmd param) {
    ResponseAssert.isTrue(CommonErrorCode.UPDATE_FAILURE,
        !Objects.equals(param.getStatus(), TeamBillCycleStatus.UNPAID));
    ResponseAssert.isTrue(CommonErrorCode.UPDATE_FAILURE,
        !Objects.equals(param.getStatus(), TeamBillCycleStatus.PAY_OUT));
    ResponseAssert.isTrue(CommonErrorCode.UPDATE_FAILURE,
        !Objects.equals(param.getStatus(), TeamBillCycleStatus.SETTLED));

    billCycleClient.updateStatus(param.getBillId(), param.getStatus(), param.getUpdateUser());

    if (StringUtils.isNotBlank(param.getRemarks())) {
      TeamRemark teamRemark = new TeamRemark()
          .setRemarkId(IdWorkerUtils.getIdStr())
          .setRemark(param.getRemarks())
          .setCreateBackUser(param.getUpdateUser())
          .setCreateTime(TimestampUtils.now());

      billCycleClient.addInternalRemarks(new AddTeamRemarkCmd()
          .setTeamId(param.getBillId())
          .setTeamRemark(teamRemark)
      );
    }

  }

  @Override
  public TeamCalculatorWorkResultDTO calculatorWork(TeamCalculatorWorkCmd param) {
    return ResponseAssert.requiredSuccess(billCycleClient.calculatorWork(param));
  }


  @Override
  public void export(HttpServletResponse response, TeamBillExportCmd param) {

    // 查询团队账单
    List<TeamBillAnchorStatisticsModelDTO> billCycleList = listBillCycleList(param);
    if (CollectionUtils.isEmpty(billCycleList)) {
      return;
    }

    List<TeamBillAnchorStatisticsModel> result = teamAppConvertor.toTeamBillAnchorStatisticsModels(
        billCycleList);
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setCharacterEncoding("utf-8");
    String fileName = URLEncoder.encode("Anchor Bill Data", StandardCharsets.UTF_8)
        .replaceAll("\\+", "%20");
    String sheetName = URLEncoder.encode("Anchor Bill Data", StandardCharsets.UTF_8)
        .replaceAll("\\+", "%20");
    response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
    try {
      EasyExcel.write(response.getOutputStream(), TeamBillAnchorStatisticsModel.class)
          .sheet(sheetName).doWrite(result);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }


  private List<TeamBillAnchorStatisticsModelDTO> listBillCycleList(TeamBillExportCmd param) {
    return ResponseAssert.requiredSuccess(billCycleClient.queryTeamBillByCondition(
            new TeamBillByConditionQryCmd()
                .setSysOrigin(param.getSysOrigin())
                .setBillStatus(param.getBillStatus())
                .setMonthDate(param.getMonthDate())
                .setRegion(param.getRegion())
        )
    );
  }
}
