package com.gxwebsoft.credit.controller;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.credit.entity.CreditCompany;
import com.gxwebsoft.credit.param.CreditCompanyImportParam;
import com.gxwebsoft.credit.param.CreditCompanyParam;
import com.gxwebsoft.credit.service.CreditCompanyService;
import com.gxwebsoft.credit.service.CreditCompanyRecordCountService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 企业控制器
 *
 * @author 科技小王子
 * @since 2025-12-17 08:28:03
 */
@Tag(name = "企业管理")
@RestController
@RequestMapping("/api/credit/credit-company")
public class CreditCompanyController extends BaseController {
  @Resource
  private CreditCompanyService creditCompanyService;

  @Resource
  private BatchImportSupport batchImportSupport;

  @Resource
  private CreditCompanyRecordCountService creditCompanyRecordCountService;

    @Operation(summary = "分页查询企业")
    @GetMapping("/page")
    public ApiResult<PageResult<CreditCompany>> page(CreditCompanyParam param) {
        // 使用关联查询
        return success(creditCompanyService.pageRel(param));
    }

    @Operation(summary = "查询全部企业")
    @GetMapping()
    public ApiResult<List<CreditCompany>> list(CreditCompanyParam param) {
        // 使用关联查询
        return success(creditCompanyService.listRel(param));
    }

    @Operation(summary = "根据id查询企业")
    @GetMapping("/{id}")
    public ApiResult<CreditCompany> get(@PathVariable("id") Integer id) {
        // 使用关联查询
        return success(creditCompanyService.getByIdRel(id));
    }

    @PreAuthorize("hasAuthority('credit:creditCompany:save')")
    @OperationLog
    @Operation(summary = "添加企业")
    @PostMapping()
    public ApiResult<?> save(@RequestBody CreditCompany creditCompany) {
        if (creditCompanyService.save(creditCompany)) {
            return success("添加成功");
        }
        return fail("添加失败");
    }

    @PreAuthorize("hasAuthority('credit:creditCompany:update')")
    @OperationLog
    @Operation(summary = "修改企业")
    @PutMapping()
    public ApiResult<?> update(@RequestBody CreditCompany creditCompany) {
        if (creditCompanyService.updateById(creditCompany)) {
            return success("修改成功");
        }
        return fail("修改失败");
    }

    @PreAuthorize("hasAuthority('credit:creditCompany:remove')")
    @OperationLog
    @Operation(summary = "删除企业")
    @DeleteMapping("/{id}")
    public ApiResult<?> remove(@PathVariable("id") Integer id) {
        if (creditCompanyService.removeById(id)) {
            return success("删除成功");
        }
        return fail("删除失败");
    }

    @PreAuthorize("hasAuthority('credit:creditCompany:save')")
    @OperationLog
    @Operation(summary = "批量添加企业")
    @PostMapping("/batch")
    public ApiResult<?> saveBatch(@RequestBody List<CreditCompany> list) {
        if (creditCompanyService.saveBatch(list)) {
            return success("添加成功");
        }
        return fail("添加失败");
    }

    @PreAuthorize("hasAuthority('credit:creditCompany:update')")
    @OperationLog
    @Operation(summary = "批量修改企业")
    @PutMapping("/batch")
    public ApiResult<?> removeBatch(@RequestBody BatchParam<CreditCompany> batchParam) {
        if (batchParam.update(creditCompanyService, "id")) {
            return success("修改成功");
        }
        return fail("修改失败");
    }

    @PreAuthorize("hasAuthority('credit:creditCompany:remove')")
    @OperationLog
    @Operation(summary = "批量删除企业")
    @DeleteMapping("/batch")
    public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
        if (creditCompanyService.removeByIds(ids)) {
            return success("删除成功");
        }
        return fail("删除失败");
    }

  /**
   * 批量导入企业
   */
  @PreAuthorize("hasAuthority('credit:creditJudiciary:save')")
  @Operation(summary = "批量导入企业")
  @PostMapping("/import")
  public ApiResult<List<String>> importBatch(@RequestParam("file") MultipartFile file,
                                             @RequestParam(value = "companyId", required = false) Integer companyId) {
    List<String> errorMessages = new ArrayList<>();
    int insertedCount = 0;
    Set<String> touchedMatchNames = new HashSet<>();

    try {
      List<CreditCompanyImportParam> list = null;
      int usedTitleRows = 0;
      int usedHeadRows = 0;
      int[][] tryConfigs = new int[][]{{1, 1}, {0, 1}, {0, 2}, {0, 3}};

      for (int[] config : tryConfigs) {
        list = filterEmptyRows(tryImport(file, config[0], config[1]));
        if (!CollectionUtils.isEmpty(list)) {
          usedTitleRows = config[0];
          usedHeadRows = config[1];
          break;
        }
      }
      if (CollectionUtils.isEmpty(list)) {
        return fail("未读取到数据，请确认模板表头与示例格式一致", null);
      }

      User loginUser = getLoginUser();
      Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
      Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
      Map<String, String> urlByName = ExcelImportSupport.readHyperlinksByHeaderKey(file, 0, usedTitleRows, usedHeadRows, "原文件导入名称");
      Map<String, String> urlByMatchName = ExcelImportSupport.readHyperlinksByHeaderKey(file, 0, usedTitleRows, usedHeadRows, "系统匹配企业名称");

      final int chunkSize = 500;
      final int mpBatchSize = 500;
      List<CreditCompany> chunkItems = new ArrayList<>(chunkSize);
      List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);

      for (int i = 0; i < list.size(); i++) {
        CreditCompanyImportParam param = list.get(i);
        try {
          CreditCompany item = convertImportParamToEntity(param);
          String link = null;
          if (item.getName() != null) {
            link = urlByName.get(item.getName().trim());
          }
          if ((link == null || link.isEmpty()) && item.getMatchName() != null) {
            link = urlByMatchName.get(item.getMatchName().trim());
          }
          if (link != null && !link.isEmpty()) {
            item.setUrl(link);
          }

          // 设置默认值
          if (item.getUserId() == null && currentUserId != null) {
            item.setUserId(currentUserId);
          }
          if (item.getTenantId() == null && currentTenantId != null) {
            item.setTenantId(currentTenantId);
          }
          if (item.getStatus() == null) {
            item.setStatus(0);
          }
          if (item.getRecommend() == null) {
            item.setRecommend(0);
          }
          if (item.getDeleted() == null) {
            item.setDeleted(0);
          }
          int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
          // 验证必填字段
          if (item.getMatchName() == null || item.getMatchName().trim().isEmpty()) {
            errorMessages.add("第" + excelRowNumber + "行：项目名称不能为空");
            continue;
          }
//          if (item.getCode() == null || item.getCode().trim().isEmpty()) {
//            errorMessages.add("第" + excelRowNumber + "行：唯一标识不能为空");
//            continue;
//          }

          touchedMatchNames.add(item.getMatchName().trim());

          chunkItems.add(item);
          chunkRowNumbers.add(excelRowNumber);
          if (chunkItems.size() >= chunkSize) {
            insertedCount += batchImportSupport.persistChunkWithFallbackCount(
              chunkItems,
              chunkRowNumbers,
              () -> {
                int delta = countInsertedByMatchName(chunkItems);
                batchImportSupport.upsertBySingleKey(
                  creditCompanyService,
                  chunkItems,
                  CreditCompany::getId,
                  CreditCompany::setId,
                  CreditCompany::getMatchName,
                  CreditCompany::getMatchName,
                  null,
                  mpBatchSize
                );
                return delta;
              },
              (rowItem, rowNumber) -> {
                boolean saved = creditCompanyService.save(rowItem);
                if (saved) {
                  return 1; // insert 入库
                }
                CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
                if (existing != null) {
                  rowItem.setId(existing.getId());
                  if (creditCompanyService.updateById(rowItem)) {
                    return 0; // update 不计入“入库”条数
                  }
                }
                throw new RuntimeException("保存失败");
              },
              errorMessages
            );
            chunkItems.clear();
            chunkRowNumbers.clear();
          }
        } catch (Exception e) {
          int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
          errorMessages.add("第" + excelRowNumber + "行：" + e.getMessage());
          e.printStackTrace();
        }
      }

      if (!chunkItems.isEmpty()) {
        insertedCount += batchImportSupport.persistChunkWithFallbackCount(
          chunkItems,
          chunkRowNumbers,
          () -> {
            int delta = countInsertedByMatchName(chunkItems);
            batchImportSupport.upsertBySingleKey(
              creditCompanyService,
              chunkItems,
              CreditCompany::getId,
              CreditCompany::setId,
              CreditCompany::getMatchName,
              CreditCompany::getMatchName,
              null,
              mpBatchSize
            );
            return delta;
          },
          (rowItem, rowNumber) -> {
            boolean saved = creditCompanyService.save(rowItem);
            if (saved) {
              return 1; // insert 入库
            }
            CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
            if (existing != null) {
              rowItem.setId(existing.getId());
              if (creditCompanyService.updateById(rowItem)) {
                return 0; // update 不计入“入库”条数
              }
            }
            throw new RuntimeException("保存失败");
          },
          errorMessages
        );
      }

      // 导入完成后，按 matchName 定位本次涉及的企业并回填“关联记录数”字段（避免 companyId/自增 id 在导入对象里拿不到）。
      if (!touchedMatchNames.isEmpty()) {
        Set<Integer> touchedCompanyIds = new HashSet<>();
        List<String> allMatchNames = new ArrayList<>(touchedMatchNames);
        final int inChunkSize = 800;
        for (int i = 0; i < allMatchNames.size(); i += inChunkSize) {
          List<String> chunk = allMatchNames.subList(i, Math.min(allMatchNames.size(), i + inChunkSize));
          List<CreditCompany> dbRows = creditCompanyService.lambdaQuery()
            .select(CreditCompany::getId)
            .in(CreditCompany::getMatchName, chunk)
            .list();
          if (!CollectionUtils.isEmpty(dbRows)) {
            for (CreditCompany row : dbRows) {
              if (row != null && row.getId() != null) {
                touchedCompanyIds.add(row.getId());
              }
            }
          }
        }
        creditCompanyRecordCountService.refreshAll(touchedCompanyIds);
      }

      if (errorMessages.isEmpty()) {
        return success("成功入库" + insertedCount + "条数据", null);
      } else {
        return success("导入完成，入库" + insertedCount + "条，失败" + errorMessages.size() + "条", errorMessages);
      }

    } catch (Exception e) {
      e.printStackTrace();
      return fail("导入失败：" + e.getMessage(), null);
    }
  }

  /**
   * 下载企业导入模板
   */
  @Operation(summary = "下载企业导入模板")
  @GetMapping("/import/template")
  public void downloadTemplate(HttpServletResponse response) throws IOException {
    List<CreditCompanyImportParam> templateList = new ArrayList<>();

    CreditCompanyImportParam example = new CreditCompanyImportParam();
    example.setName("示例客户");
    example.setCode("C0001");
    example.setMatchName("匹配名称");
    example.setRegistrationStatus("登记状态");
    example.setLegalPerson("法定代表人");
    example.setRegisteredCapital("注册资本");
    example.setPaidinCapital("实缴资本");
    example.setEstablishDate("成立日期");
    example.setAddress("地址");
    example.setTel("电话");
    example.setMoreTel("更多电话");
    example.setEmail("邮箱");
    example.setMoreEmail("更多邮箱");
    example.setProvince("省");
    example.setCity("市");
    example.setRegion("区");
    example.setInstitutionType("机构类型");
    example.setTaxpayerCode("纳税人识别号");
    example.setRegistrationNumber("注册号");
    example.setOrganizationalCode("组织机构代码");
    example.setNumberOfInsuredPersons("参保人数");
    example.setAnnualReport("入库时间");
    example.setBusinessTerm("营业期限");
    example.setNationalStandardIndustryCategories("国标行业门类");
    example.setNationalStandardIndustryCategories2("国标行业大类");
    example.setNationalStandardIndustryCategories3("国标行业中类");
    example.setNationalStandardIndustryCategories4("国标行业小类");
    example.setNationalStandardIndustryCategories5("企查查行业门类");
    example.setNationalStandardIndustryCategories6("企查查行业大类");
    example.setNationalStandardIndustryCategories7("企查查行业中类");
    example.setNationalStandardIndustryCategories8("企查查行业小类");
    example.setCompanySize("企业规模");
    example.setFormerName("曾用名");
    example.setEnglishName("英文名");
    example.setDomain("官网");
    example.setMailingAddress("通信地址");
    example.setCompanyProfile("企业简介");
    example.setNatureOfBusiness("经营范围");
    example.setRegistrationAuthority("登记机关");
    example.setTaxpayerQualification("纳税人资质");
    example.setLatestAnnualReportYear("最新年报年份");
    example.setLatestAnnualReportOnOperatingRevenue("最新年报营业额");
    example.setEnterpriseScoreCheck("企查分");
    example.setCreditRating("信用等级");
    example.setCechnologyScore("科创分");
    example.setCechnologyLevel("科创等级");
    example.setSmallEnterprise("是否小微企业");
    templateList.add(example);

    ExportParams exportParams = new ExportParams("一级企业主表导入模板", "企业");

    Workbook workbook = ExcelExportUtil.exportExcel(exportParams, CreditCompanyImportParam.class, templateList);

    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setHeader("Content-Disposition", "attachment; filename=credit_company_import_template.xlsx");

    workbook.write(response.getOutputStream());
    workbook.close();
  }

  private List<CreditCompanyImportParam> tryImport(MultipartFile file, int titleRows, int headRows) throws Exception {
    ImportParams importParams = new ImportParams();
    importParams.setTitleRows(titleRows);
    importParams.setHeadRows(headRows);
    importParams.setStartSheetIndex(0);
    importParams.setSheetNum(1);
    return ExcelImportUtil.importExcel(file.getInputStream(), CreditCompanyImportParam.class, importParams);
  }

  /**
   * 过滤掉完全空白的导入行，避免空行导致导入失败
   */
  private List<CreditCompanyImportParam> filterEmptyRows(List<CreditCompanyImportParam> rawList) {
    if (CollectionUtils.isEmpty(rawList)) {
      return rawList;
    }
    rawList.removeIf(this::isEmptyImportRow);
    return rawList;
  }

  private boolean isEmptyImportRow(CreditCompanyImportParam param) {
    if (param == null) {
      return true;
    }
    return isBlank(param.getName())
      && isBlank(param.getCode());
  }

  private boolean isBlank(String value) {
    return value == null || value.trim().isEmpty();
  }

  /**
   * 入库条数以 insert 为准：matchName 在数据库中不存在时计 1，否则计 0。
   *
   * <p>统计口径需与批量 upsert 的匹配字段保持一致（matchName）。</p>
   */
  private int countInsertedByMatchName(List<CreditCompany> items) {
    if (CollectionUtils.isEmpty(items)) {
      return 0;
    }
    List<String> matchNames = new ArrayList<>(items.size());
    for (CreditCompany item : items) {
      if (item == null) {
        continue;
      }
      String key = item.getMatchName();
      if (!isBlank(key)) {
        matchNames.add(key.trim());
      }
    }
    if (matchNames.isEmpty()) {
      return 0;
    }

    Set<String> existing = new HashSet<>();
    List<CreditCompany> dbRows = creditCompanyService.lambdaQuery()
      .select(CreditCompany::getMatchName)
      .in(CreditCompany::getMatchName, matchNames)
      .list();
    if (!CollectionUtils.isEmpty(dbRows)) {
      for (CreditCompany row : dbRows) {
        String v = row != null ? row.getMatchName() : null;
        if (!isBlank(v)) {
          existing.add(v.trim());
        }
      }
    }

    int count = 0;
    for (CreditCompany item : items) {
      String key = item != null ? item.getMatchName() : null;
      if (!isBlank(key) && !existing.contains(key.trim())) {
        count++;
      }
    }
    return count;
  }

  /**
   * 将CreditCompanyImportParam转换为CreditCompany实体
   */
  private CreditCompany convertImportParamToEntity(CreditCompanyImportParam param) {
    CreditCompany entity = new CreditCompany();

    entity.setCode(param.getCode());
    entity.setName(param.getName());
    entity.setMatchName(param.getMatchName());
    entity.setRegistrationStatus(param.getRegistrationStatus());
    entity.setLegalPerson(param.getLegalPerson());
    entity.setRegisteredCapital(param.getRegisteredCapital());
    entity.setPaidinCapital(param.getPaidinCapital());
    entity.setEstablishDate(param.getEstablishDate());
    entity.setAddress(param.getAddress());
    entity.setTel(param.getTel());
    entity.setMoreTel(param.getMoreTel());
    entity.setEmail(param.getEmail());
    entity.setMoreEmail(param.getMoreEmail());
    entity.setProvince(param.getProvince());
    entity.setCity(param.getCity());
    entity.setRegion(param.getRegion());
    entity.setInstitutionType(param.getInstitutionType());
    entity.setTaxpayerCode(param.getTaxpayerCode());
    entity.setRegistrationNumber(param.getRegistrationNumber());
    entity.setOrganizationalCode(param.getOrganizationalCode());
    entity.setNumberOfInsuredPersons(param.getNumberOfInsuredPersons());
    entity.setAnnualReport(param.getAnnualReport());
    entity.setBusinessTerm(param.getBusinessTerm());
    entity.setNationalStandardIndustryCategories(param.getNationalStandardIndustryCategories());
    entity.setNationalStandardIndustryCategories2(param.getNationalStandardIndustryCategories2());
    entity.setNationalStandardIndustryCategories3(param.getNationalStandardIndustryCategories3());
    entity.setNationalStandardIndustryCategories4(param.getNationalStandardIndustryCategories4());
    entity.setNationalStandardIndustryCategories5(param.getNationalStandardIndustryCategories5());
    entity.setNationalStandardIndustryCategories6(param.getNationalStandardIndustryCategories6());
    entity.setNationalStandardIndustryCategories7(param.getNationalStandardIndustryCategories7());
    entity.setNationalStandardIndustryCategories8(param.getNationalStandardIndustryCategories8());
    entity.setCompanySize(param.getCompanySize());
    entity.setFormerName(param.getFormerName());
    entity.setEnglishName(param.getEnglishName());
    entity.setDomain(param.getDomain());
    entity.setMailingAddress(param.getMailingAddress());
    entity.setCompanyProfile(param.getCompanyProfile());
    entity.setNatureOfBusiness(param.getNatureOfBusiness());
    entity.setRegistrationAuthority(param.getRegistrationAuthority());
    entity.setTaxpayerQualification(param.getTaxpayerQualification());
    entity.setLatestAnnualReportYear(param.getLatestAnnualReportYear());
    entity.setLatestAnnualReportOnOperatingRevenue(param.getLatestAnnualReportOnOperatingRevenue());
    entity.setEnterpriseScoreCheck(param.getEnterpriseScoreCheck());
    entity.setCreditRating(param.getCreditRating());
    entity.setCechnologyScore(param.getCechnologyScore());
    entity.setCechnologyLevel(param.getCechnologyLevel());
    entity.setSmallEnterprise(param.getSmallEnterprise());

    return entity;
  }


}
