package cn.palmte.work.controller.backend;

import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import cn.palmte.work.model.Admin;
import cn.palmte.work.model.DeptRepository;
import cn.palmte.work.model.Project;
import cn.palmte.work.model.ProjectBudgetIncomeDetail;
import cn.palmte.work.model.ProjectRepository;
import cn.palmte.work.model.enums.CooperationType;
import cn.palmte.work.model.enums.Enumerable;
import cn.palmte.work.model.enums.ProcessStatus;
import cn.palmte.work.model.enums.ProcessType;
import cn.palmte.work.model.enums.ProjectType;
import cn.palmte.work.model.enums.SealType;
import cn.palmte.work.model.process.ProcurementContract;
import cn.palmte.work.model.process.ProjectProcess;
import cn.palmte.work.model.process.ProjectProcessRepository;
import cn.palmte.work.model.process.SaleContract;
import cn.palmte.work.model.process.SealTypeArray;
import cn.palmte.work.model.process.form.ProcessQueryForm;
import cn.palmte.work.model.process.form.SaleContractDetailForm;
import cn.palmte.work.model.process.form.SaleContractProcessForm;
import cn.palmte.work.service.ProjectBudgetService;
import cn.palmte.work.utils.InterfaceUtil;
import lombok.Builder;
import lombok.Data;
import lombok.RequiredArgsConstructor;

/**
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
 * @since 1.0 2022/12/8 11:03
 */
@Controller
@RequiredArgsConstructor
@RequestMapping("/process")
public class ProcessController {
  static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

  private final DeptRepository deptRepository;
  private final ProjectRepository projectRepository;
  private final ProjectBudgetService projectBudgetService;

  private final JdbcTemplate jdbcTemplate;
  private final EntityManager entityManager;

  private final ProjectProcessRepository repository;

  static class FormMetadata {
    // 部门

    // 印章类型
    public Map<String, String> sealTypes;

    // 税率
    public List<Integer> taxRate;
  }

  @ResponseBody
  @GetMapping("/form/metadata")
  public FormMetadata metadata() {
    FormMetadata metadata = new FormMetadata();
    metadata.sealTypes = Arrays.stream(SealType.values())
            .collect(Collectors.toMap(SealType::getDescription, Enum::name));
    metadata.taxRate = Arrays.asList(0, 1, 3, 4, 5, 6, 9, 10, 13);
    return metadata;
  }

  /**
   * 新建流程
   */
  @GetMapping("/new")
  public String newProcess(Model model) {
    model.addAttribute("sealTypes", SealType.values());
    model.addAttribute("taxRate", Arrays.asList(0, 1, 3, 4, 5, 6, 9, 10, 13));
    return "/admin/business/process-new";
  }

  /**
   * 已办流程
   */
  @GetMapping("/completed")
  public String completed(Model model) {
    model.addAttribute("processTypes", ProcessType.values());
    model.addAttribute("processStatus", ProcessStatus.values());
    return "/admin/business/process-completed";
  }

  /**
   * 待我审核
   */
  @GetMapping("/review")
  public String review(Model model) {
    model.addAttribute("processTypes", ProcessType.values());
    return "/admin/business/process-review";
  }

  @ResponseBody
  @GetMapping("/projects")
  public List<Map<String, Object>> query(@RequestParam String q) {
    return projectRepository.findByProjectNoOrName(q)
            .stream()
            .map(project -> {
              HashMap<String, Object> map = new HashMap<>();
              map.put("id", project.getId());
              map.put("name", project.getName());
              return map;
            })
            .collect(Collectors.toList());
  }

  @Builder
  public static class ProjectReturnValue {
    public final String applyDate = LocalDate.now().format(formatter);
    public String projectNo;
    public Integer projectId;
    public String projectName;

    // 申请人
    public String applyPersonName;

    // 项目类型
    public String projectType;

    // 合作类型
    public String cooperationType;

    // 合同金额
    public BigDecimal contractAmount;

    public List<ProjectBudgetIncomeDetail> incomeDetails;

    // FIXME 垫资
    // 是否垫资
    public final String isPrepaid = "是";

    // 垫资金额
    public final String repaidAmount = "50000元";

    // 预算毛利率
    public final String budgetGrossMargin = "3.9%";
  }

  @ResponseBody
  @GetMapping("/projects/{id}")
  public ProjectReturnValue query(@PathVariable int id) {
    Project project = projectRepository.findById(id);
    Admin admin = InterfaceUtil.getAdmin();

    // 可以在对应表数据查询 是否存在再启用
    List<ProjectBudgetIncomeDetail> incomeDetails = projectBudgetService.getBudgetIncomeDetail(project);
    return ProjectReturnValue.builder()
            .projectId(project.getId())
            .incomeDetails(incomeDetails)
            .projectName(project.getName())
            .projectNo(project.getProjectNo())
            .applyPersonName(admin.getRealName())
            .contractAmount(project.getContractAmount())
            .projectType(Enumerable.of(ProjectType.class, project.getType()).getDescription())
            .cooperationType(Enumerable.of(CooperationType.class, project.getCooperateType()).getDescription())
            .build();
  }

  // 销售合同流程

  @ResponseBody
  @PostMapping
  @Transactional
  public void post(@RequestBody @Valid SaleContractProcessForm form) {
    ProjectProcess entity = new ProjectProcess();
    BeanUtils.copyProperties(form, entity, "sealTypes", "applyDate", "applyDept");
    entity.setApplyDate(LocalDate.parse(form.getApplyDate(), formatter));
    entity.setSealTypes(SealTypeArray.of(form.getSealTypes()));
    entity.setApplyDept(String.join(",", form.getApplyDept()));
    Admin admin = InterfaceUtil.getAdmin();
    entity.setApplyPersonId(admin.getId());

    if (entity.getStatus() == null) {
      entity.setStatus(ProcessStatus.to_be_audit);
    }

    entityManager.persist(entity);

    if (entity.getProcessType() != null) {
      switch (entity.getProcessType()) {
        case sale_contract:
          SaleContract saleContract = SaleContract.from(form);
          saleContract.setProcessId(entity.getId());
          entityManager.persist(saleContract);
          break;
        case business_procurement:
          ProcurementContract procurementContract = ProcurementContract.from(form);
          procurementContract.setProcessId(entity.getId());
          entityManager.persist(procurementContract);
          break;
        default:
          throw new UnsupportedOperationException("还不支持");
      }
    }

    List<SaleContractDetailForm> incomeDetails = form.getIncomeDetails();
    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();
                }
              });
    }
  }

  @ResponseBody
  @PostMapping("/query")
  public Page<ProjectProcess> list(@RequestBody ProcessQueryForm form, Pageable pageable) {
    // 从 0 开始
    PageRequest pageRequest = new PageRequest(pageable.getPageNumber() - 1, pageable.getPageSize());

    ProjectProcess projectProcess = new ProjectProcess();
    BeanUtils.copyProperties(form, projectProcess);
    projectProcess.setStatus(form.getProcessStatus());

    ExampleMatcher exampleMatcher = ExampleMatcher.matching()
            .withMatcher("contractNo", ExampleMatcher.GenericPropertyMatcher::contains)
            .withMatcher("projectTitle", ExampleMatcher.GenericPropertyMatcher::contains)
            .withMatcher("applyPersonName", ExampleMatcher.GenericPropertyMatcher::contains);

    // 只展示自己创建的草稿
    // Admin admin = InterfaceUtil.getAdmin();
    // projectProcess.setApplyPersonId(admin.getId());

    return repository.findAll(Example.of(projectProcess, exampleMatcher), pageRequest);
  }

  // 审核
  @Data
  static class AuditForm {

    // 流程ID
    private Integer processId;

    // 审核意见
    @NotNull
    private String auditOpinion;

    // 审核状态
    private ProcessStatus processStatus;

  }

  @ResponseBody
  @PostMapping("/audit")
  public void audit(@RequestBody @Valid AuditForm form) {

  }

  @ResponseBody
  @GetMapping("/detail/{id}")
  public ProjectProcess get(@PathVariable("id") int id, Model model) {
//    model.addAttribute();
    return entityManager.find(ProjectProcess.class, id);
  }

  @ResponseBody
  @PostMapping("/revoke/{id}")
  public void revoke(@PathVariable("id") int id) {
    // TODO 发起申请的人，在第一个人还没审批的情况下可以撤回
    jdbcTemplate.update("update project_process set `status` =? where id =? ", ProcessStatus.draft.getValue(), id);
  }

  @ResponseBody
  @DeleteMapping("/{id}")
  public void delete(@PathVariable("id") int id) {
    Admin admin = InterfaceUtil.getAdmin();
    Integer applyUserId = admin.getId();
    int update = jdbcTemplate.update("delete from project_process where id =? and apply_person_id=?", id, applyUserId);
    if (update != 1) {
      throw new RuntimeException("删除的流程不存在或者不属于自己");
    }
  }

  /**
   * 查看审核流程
   */
  @ResponseBody
  @GetMapping("/audits/{id}")
  public List<Map<String, String>> showAudits(@PathVariable("id") int id) {
    ArrayList<Map<String, String>> ret = new ArrayList<>();

    ret.add(create("2021-01-03 12:07:45", "提交审核"));
    ret.add(create("2021-01-28 12:15:05", "工程运维管理部孙果审核已通过"));
    ret.add(create("2021-02-03 22:01:12", "系统产品集成部尹浩审核已通过"));
    ret.add(create("2021-03-03 22:05:12", "财务部主管何丹审核已通过"));

    return ret;
  }

  private Map<String, String> create(String timestamp, String content) {
    Map<String, String> map = new HashMap<>();
    map.put("content", content);
    map.put("timestamp", timestamp);
    return map;
  }

}
