package cn.palmte.work.service;

import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;

import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.config.activiti.ActProjectTypeEnum;
import cn.palmte.work.model.Admin;
import cn.palmte.work.model.AdminRepository;
import cn.palmte.work.model.Dept;
import cn.palmte.work.model.DeptRepository;
import cn.palmte.work.model.ProcurementType;
import cn.palmte.work.model.ProcurementTypeRepository;
import cn.palmte.work.model.Project;
import cn.palmte.work.model.ProjectBudgetCostDetail;
import cn.palmte.work.model.ProjectRepository;
import cn.palmte.work.model.enums.ProcessStatus;
import cn.palmte.work.model.process.BudgetPurchaseAmount;
import cn.palmte.work.model.process.BudgetPurchaseDetail;
import cn.palmte.work.model.process.ProcurementContract;
import cn.palmte.work.model.process.ProcurementDetail;
import cn.palmte.work.model.process.ProjectProcess;
import cn.palmte.work.model.process.SaleContract;
import cn.palmte.work.model.process.SupplierMaterial;
import cn.palmte.work.model.process.form.SaleContractDetailForm;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

/**
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
 * @since 2.0 2022/12/23 09:39
 */
@Service
@RequiredArgsConstructor
public class ProjectProcessService {

  private final JdbcTemplate jdbcTemplate;
  private final EntityManager entityManager;
  private final ProjectInstanceService projectInstanceService;
  private final ProjectRepository projectRepository;

  private final DeptRepository deptRepository;
  private final AdminRepository userRepository;
  private final ProcurementTypeRepository procurementTypeRepository;

  @Data
  static class DeptReturnValue {

    private int id;
    private int leaderId;

    private String leaderName;

    private String name;

    private List<DeptReturnValue> children;

    public void addChildren(DeptReturnValue child) {
      if (children == null) {
        children = new ArrayList<>();
      }
      children.add(child);
    }
  }

  public List<DeptReturnValue> filterDept() {
    List<Dept> deptList = deptRepository.findEnable();
    List<Dept> level1 = filterByLevel(deptList, 1);
    List<Dept> level2 = filterByLevel(deptList, 2);
    List<Dept> level3 = filterByLevel(deptList, 3);

    List<DeptReturnValue> returnValues = new ArrayList<>();
    for (Dept dept : level1) {
      // 只需要一级领导
      Admin leader = userRepository.getAdminById(dept.getManagerId());
      String leaderName = leader.getRealName();
      DeptReturnValue returnValue = createReturnValue(dept, leaderName);
      for (Dept dept2 : level2) {
        if (Objects.equals(dept2.getParentId(), dept.getId())) {
          DeptReturnValue returnValue2 = createReturnValue(dept2, leaderName);
          for (Dept dept3 : level3) {
            if (Objects.equals(dept3.getParentId(), dept2.getId())) {
              returnValue2.addChildren(createReturnValue(dept3, leaderName));
            }
          }
          returnValue.addChildren(returnValue2);
        }
      }
      returnValues.add(returnValue);
    }
    return returnValues;
  }

  private static List<Dept> filterByLevel(List<Dept> deptList, int level) {
    ArrayList<Dept> ret = new ArrayList<>();
    for (Dept dept : deptList) {
      if (Objects.equals(dept.getLevel(), level)) {
        ret.add(dept);
      }
    }
    return ret;
  }

  private DeptReturnValue createReturnValue(Dept dept, String leaderName) {
    DeptReturnValue returnValue = new DeptReturnValue();

    returnValue.setId(dept.getId());
    returnValue.setName(dept.getName());
    returnValue.setLeaderId(dept.getManagerId());
    returnValue.setLeaderName(leaderName);
    return returnValue;
  }

  /**
   * 更新流程 审批人,和状态
   *
   * @param processId 流程Id
   * @param auditId 审批人ID
   * @param status 流程状态 可以为空,为空的时候不修改
   */
  public void updateAudit(int processId, ProcessStatus status, List<Integer> auditId) {
    String currentAudit = getCurrentAudit(auditId);
    String currentAuditId = auditId.stream().map(String::valueOf).collect(Collectors.joining(","));
    if (status != null) {
      if (!CollectionUtils.isEmpty(auditId)) {
        jdbcTemplate.update(
                "update project_process set current_audit=?,current_audit_id=?,status=? where id=?", currentAudit, currentAuditId, status.getValue(), processId);
      }
    }
    else {
      jdbcTemplate.update("update project_process set current_audit=?,current_audit_id=?  where id=?", currentAudit, currentAuditId, processId);
    }
  }

  private String getCurrentAudit(List<Integer> auditId) {
    TypedQuery<Admin> query = entityManager.createQuery("from Admin where id in (:ids)", Admin.class);
    query.setParameter("ids", auditId);
    List<Admin> resultList = query.getResultList();
    return resultList.stream().map(Admin::getRealName)
            .collect(Collectors.joining(","));
  }

  /**
   * 只更新状态
   */
  public void updateProcessStatus(int processId, ProcessStatus status) {
    jdbcTemplate.update("update project_process set `status`=? where id=?", status.getValue(), processId);
  }

  /**
   * 根据流程ID查询销售合同
   *
   * @param processId 流程ID
   * @return 销售合同
   */
  public SaleContract findSaleContract(int processId) {
    TypedQuery<SaleContract> query = entityManager.createQuery(
            "from SaleContract where processId=:processId", SaleContract.class);
    query.setParameter("processId", processId);
    return query.getSingleResult();
  }

  /**
   * 根据流程ID查询采购合同
   *
   * @param processId 流程ID
   * @return 采购合同
   */
  public ProcurementContract findProcurementContract(int processId) {
    TypedQuery<ProcurementContract> query = entityManager.createQuery(
            "from ProcurementContract where processId=:processId", ProcurementContract.class);
    query.setParameter("processId", processId);
    return query.getSingleResult();
  }

  public List<SupplierMaterial> getSupplierMaterials(int processId) {
    TypedQuery<SupplierMaterial> query = entityManager.createQuery(
            "from SupplierMaterial where processId=:processId", SupplierMaterial.class);
    query.setParameter("processId", processId);
    return query.getResultList();
  }

  /**
   * 获取 采购清单
   *
   * @param projectId 项目ID
   * @param processId 流程ID 一般用于获取详情
   */
  public List<ProcurementDetail> getProcurementDetails(int projectId, Integer processId) {
    List<ProcurementDetail> ret = new ArrayList<>();

    List<ProjectBudgetCostDetail> costDetails = getCostDetails(projectId);
    for (ProjectBudgetCostDetail costDetail : costDetails) {
      ProcurementDetail detail = new ProcurementDetail();
      BeanUtils.copyProperties(costDetail, detail);
      // 可能为 0
      BigDecimal amountAlready = getAmountAlready(costDetail.getId());
      detail.setAmountAlready(amountAlready);

      if (processId != null) {
        // 根据 processId 确定唯一的 BudgetPurchaseAmount 用作获取详情 , 可能还未创建
        BudgetPurchaseAmount purchaseAmount = getPurchaseAmount(projectId, processId);
        if (purchaseAmount != null) {
          Integer amountId = purchaseAmount.getId();
          List<BudgetPurchaseDetail> purchaseDetails = getBudgetPurchaseDetails(amountId);
          detail.setPurchaseDetails(purchaseDetails);
        }
      }
      // TODO 查询太频繁
      detail.setCategory(getCategory(costDetail));

      detail.setBudgetCostId(costDetail.getId());
      ret.add(detail);
    }

    return ret;
  }

  private String getCategory(ProjectBudgetCostDetail costDetail) {
    int category = costDetail.getCategory();
    TypedQuery<ProcurementType> query = entityManager.createQuery(
            "from ProcurementType where id=:category and isDeleted=0 and enabled=1", ProcurementType.class);
    query.setParameter("category", category);
    try {
      ProcurementType procurementType = query.getSingleResult();
      return procurementType.getName();
    }
    catch (NoResultException e) {
      return String.valueOf(category);
    }
  }

  private List<BudgetPurchaseDetail> getBudgetPurchaseDetails(int amountId) {
    TypedQuery<BudgetPurchaseDetail> query = entityManager.createQuery(
            "from BudgetPurchaseDetail where amountId=:amountId", BudgetPurchaseDetail.class);
    query.setParameter("amountId", amountId);
    return query.getResultList();
  }

  private List<ProjectBudgetCostDetail> getCostDetails(int projectId) {
    TypedQuery<ProjectBudgetCostDetail> query = entityManager.createQuery(
            "from ProjectBudgetCostDetail where projectId=:projectId", ProjectBudgetCostDetail.class);
    query.setParameter("projectId", projectId);
    return query.getResultList();
  }

  public BudgetPurchaseAmount getPurchaseAmount(int budgetCostId, Integer processId) {
    TypedQuery<BudgetPurchaseAmount> amountQuery = entityManager.createQuery(
            "from BudgetPurchaseAmount where budgetCostId=:budgetCostId and processId=:processId", BudgetPurchaseAmount.class);
    amountQuery.setParameter("budgetCostId", budgetCostId);
    amountQuery.setParameter("processId", processId);
    try {
      return amountQuery.getSingleResult();
    }
    catch (NoResultException e) {
      return null;
    }
  }

  public List<BudgetPurchaseAmount> getPurchaseAmountList(int budgetCostId) {
    TypedQuery<BudgetPurchaseAmount> amountQuery = entityManager.createQuery(
            "from BudgetPurchaseAmount where budgetCostId=:budgetCostId", BudgetPurchaseAmount.class);
    amountQuery.setParameter("budgetCostId", budgetCostId);
    return amountQuery.getResultList();
  }

  public BigDecimal getAmountAlready(int budgetCostId) {
    return getPurchaseAmountList(budgetCostId).stream()
            .map(BudgetPurchaseAmount::getAmountAlready)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
  }

  public ProjectProcess getById(int id) {
    return entityManager.find(ProjectProcess.class, id);
  }

  /**
   * 更新 销售合同 的 质保期
   */
  public void updateIncomeDetails(List<SaleContractDetailForm> incomeDetails) {
    if (!CollectionUtils.isEmpty(incomeDetails)) {
      jdbcTemplate.batchUpdate("update project_budget_income_detail set expiration_date =? where id =? ",
              new BatchPreparedStatementSetter() {

                @Override
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                  SaleContractDetailForm detailForm = incomeDetails.get(i);
                  ps.setString(1, detailForm.getExpirationDate());
                  ps.setInt(2, detailForm.getId());
                }

                @Override
                public int getBatchSize() {
                  return incomeDetails.size();
                }
              });
    }
  }

  public BigDecimal getProjectRepaidAmount(int projectId) {
    Map<String, Object> map = jdbcTemplate.queryForMap(
            "select ifnull(sum(underwritten_plan), 0) repaidAmount from project_budget_plan_detail where project_id = ?", projectId);
    return (BigDecimal) map.values().iterator().next();
  }

  /**
   * 发起流程审批
   */
  public void startAuditProgress(ProjectProcess entity) {
    HashMap<String, Object> variables = new HashMap<>();
    Project project = projectRepository.findById(entity.getProjectId());
    // 是否垫资
    variables.put("isPrepaid", isProjectPrepaid(project));
    // 垫资金额
    BigDecimal repaidAmount = getProjectRepaidAmount(entity.getProjectId());
    variables.put("repaidAmount", repaidAmount);
    // 合同金额
    variables.put("contractAmount", project.getContractAmount() == null ? 0 : project.getContractAmount());
    // 项目类型
    variables.put("projectType", project.getType());
    // 合作类型
    variables.put("cooperationType", project.getCooperateType() == null ? 0 : project.getCooperateType());
    // 部门领导ID
    variables.put(ActConstant.DEPT_LEADER_ID, entity.getApplyDeptLeaderId());

    startAuditProgress(entity, variables);
  }

  /**
   * 是否垫资
   */
  public boolean isProjectPrepaid(Project project) {
    return project.getUnderwrittenMode() == 2 || project.getUnderwrittenMode() == 3;
  }

  /**
   * 发起流程审批
   */
  @SneakyThrows
  public void startAuditProgress(ProjectProcess entity, Map<String, Object> variables) {
    if (entity.getStatus() == ProcessStatus.to_be_audit) {
      switch (entity.getProcessType()) {
        case sale_contract:
          projectInstanceService.startProcessByProjectId(entity.getId(), ActProjectTypeEnum.SALE_CONTRACT, variables);
          break;
        case procurement_contract:
          projectInstanceService.startProcessByProjectId(entity.getId(), ActProjectTypeEnum.BUSINESS_PURCHASE, variables);
          break;
        default:
          throw new UnsupportedOperationException("还不支持");
      }
    }
  }

}