package com.gxwebsoft.credit.controller;

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.CreditCustomer;
import com.gxwebsoft.credit.param.CreditCustomerImportParam;
import com.gxwebsoft.credit.param.CreditCustomerParam;
import com.gxwebsoft.credit.service.CreditCompanyService;
import com.gxwebsoft.credit.service.CreditCompanyRecordCountService;
import com.gxwebsoft.credit.service.CreditCustomerService;
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.dao.DataIntegrityViolationException;
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.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 客户控制器
 *
 * @author 科技小王子
 * @since 2025-12-21 21:20:58
 */
@Tag(name = "客户管理")
@RestController
@RequestMapping("/api/credit/credit-customer")
public class CreditCustomerController extends BaseController {
    @Resource
    private CreditCustomerService creditCustomerService;

    @Resource
    private BatchImportSupport batchImportSupport;

    @Resource
    private CreditCompanyService creditCompanyService;

    @Resource
    private CreditCompanyRecordCountService creditCompanyRecordCountService;

    @Operation(summary = "分页查询客户")
    @GetMapping("/page")
    public ApiResult<PageResult<CreditCustomer>> page(CreditCustomerParam param) {
        // 使用关联查询
        return success(creditCustomerService.pageRel(param));
    }

    @Operation(summary = "查询全部客户")
    @GetMapping()
    public ApiResult<List<CreditCustomer>> list(CreditCustomerParam param) {
        // 使用关联查询
        return success(creditCustomerService.listRel(param));
    }

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

    @PreAuthorize("hasAuthority('credit:creditCustomer:save')")
    @OperationLog
    @Operation(summary = "添加客户")
    @PostMapping()
    public ApiResult<?> save(@RequestBody CreditCustomer creditCustomer) {
        if (creditCustomerService.save(creditCustomer)) {
            return success("添加成功");
        }
        return fail("添加失败");
    }

    @PreAuthorize("hasAuthority('credit:creditCustomer:update')")
    @OperationLog
    @Operation(summary = "修改客户")
    @PutMapping()
    public ApiResult<?> update(@RequestBody CreditCustomer creditCustomer) {
        if (creditCustomerService.updateById(creditCustomer)) {
            return success("修改成功");
        }
        return fail("修改失败");
    }

    @PreAuthorize("hasAuthority('credit:creditCustomer:remove')")
    @OperationLog
    @Operation(summary = "删除客户")
    @DeleteMapping("/{id}")
    public ApiResult<?> remove(@PathVariable("id") Integer id) {
        if (batchImportSupport.hardRemoveById(CreditCustomer.class, id)) {
            return success("删除成功");
        }
        return fail("删除失败");
    }

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

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

    @PreAuthorize("hasAuthority('credit:creditCustomer:remove')")
    @OperationLog
    @Operation(summary = "批量删除客户")
    @DeleteMapping("/batch")
    public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
        if (batchImportSupport.hardRemoveByIds(CreditCustomer.class, ids)) {
            return success("删除成功");
        }
        return fail("删除失败");
    }

    /**
     * 根据企业名称匹配企业并更新 companyId（匹配 CreditCompany.name / CreditCompany.matchName）
     *
     * <p>默认仅更新 companyId=0 的记录；如需覆盖更新，传 onlyNull=false。</p>
     */
    @PreAuthorize("hasAuthority('credit:creditCustomer:update')")
    @OperationLog
    @Operation(summary = "根据企业名称匹配并更新companyId")
    @PostMapping("/company-id/refresh")
    public ApiResult<Map<String, Object>> refreshCompanyIdByCompanyName(
        @RequestParam(value = "onlyNull", required = false, defaultValue = "true") Boolean onlyNull,
        @RequestParam(value = "limit", required = false) Integer limit
    ) {
        User loginUser = getLoginUser();
        Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;

        BatchImportSupport.CompanyIdRefreshStats stats = batchImportSupport.refreshCompanyIdByCompanyName(
            creditCustomerService,
            creditCompanyService,
            currentTenantId,
            onlyNull,
            limit,
            CreditCustomer::getId,
            CreditCustomer::setId,
            CreditCustomer::getName,
            CreditCustomer::getCompanyId,
            CreditCustomer::setCompanyId,
            CreditCustomer::getHasData,
            CreditCustomer::setHasData,
            CreditCustomer::getTenantId,
            CreditCustomer::new
        );

        if (!stats.anyDataRead) {
            return success("无可更新数据", stats.toMap());
        }
        return success("更新完成，更新" + stats.updated + "条", stats.toMap());
    }

    /**
     * 批量导入客户
     */
    @PreAuthorize("hasAuthority('credit:creditCustomer: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 successCount = 0;
        Set<Integer> touchedCompanyIds = new HashSet<>();

        try {
            int sheetIndex = ExcelImportSupport.findSheetIndex(file, "客户", 4);
            ExcelImportSupport.ImportResult<CreditCustomerImportParam> importResult = ExcelImportSupport.read(
                file, CreditCustomerImportParam.class, this::isEmptyImportRow, sheetIndex);
            List<CreditCustomerImportParam> list = importResult.getData();
            int usedTitleRows = importResult.getTitleRows();
            int usedHeadRows = importResult.getHeadRows();
            int usedSheetIndex = importResult.getSheetIndex();

            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.readUrlByKey(file, usedSheetIndex, usedTitleRows, usedHeadRows, "客户");

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

            for (int i = 0; i < list.size(); i++) {
                CreditCustomerImportParam param = list.get(i);
                int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
                try {
                    CreditCustomer item = convertImportParamToEntity(param);
                    if (!ImportHelper.isBlank(item.getName())) {
                        item.setName(item.getName().trim());
                        String link = urlByName.get(item.getName());
                        if (!ImportHelper.isBlank(link)) {
                            item.setUrl(link.trim());
                        }
                    }

                    if (item.getCompanyId() == null && companyId != null) {
                        item.setCompanyId(companyId);
                    }
                    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);
                    }

                    if (ImportHelper.isBlank(item.getName())) {
                        errorMessages.add("第" + excelRowNumber + "行：客户不能为空");
                        continue;
                    }

                    if (item.getCompanyId() != null && item.getCompanyId() > 0) {
                        touchedCompanyIds.add(item.getCompanyId());
                    }

                    chunkItems.add(item);
                    chunkRowNumbers.add(excelRowNumber);
                    if (chunkItems.size() >= chunkSize) {
                        successCount += persistImportChunk(chunkItems, chunkRowNumbers, mpBatchSize, errorMessages);
                        chunkItems.clear();
                        chunkRowNumbers.clear();
                    }
                } catch (Exception e) {
                    errorMessages.add("第" + excelRowNumber + "行：" + e.getMessage());
                    e.printStackTrace();
                }
            }

            if (!chunkItems.isEmpty()) {
                successCount += persistImportChunk(chunkItems, chunkRowNumbers, mpBatchSize, errorMessages);
            }

            creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.CUSTOMER, touchedCompanyIds);

            if (errorMessages.isEmpty()) {
                return success("成功导入" + successCount + "条数据", null);
            } else {
                return success("导入完成，成功" + successCount + "条，失败" + 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<CreditCustomerImportParam> templateList = new ArrayList<>();

        CreditCustomerImportParam example = new CreditCustomerImportParam();
        example.setName("示例客户");
        example.setStatusTxt("合作中");
        example.setPrice("88.8");
        example.setPublicDate("2024-01-01");
        example.setDataSource("公开渠道");
        example.setComments("备注信息");
        templateList.add(example);

        Workbook workbook = ExcelImportSupport.buildTemplate("客户导入模板", "客户", CreditCustomerImportParam.class, templateList);

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

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

    private boolean isEmptyImportRow(CreditCustomerImportParam param) {
        if (param == null) {
            return true;
        }
        return ImportHelper.isBlank(param.getName())
            && ImportHelper.isBlank(param.getStatusTxt())
            && ImportHelper.isBlank(param.getPrice());
    }

    private CreditCustomer convertImportParamToEntity(CreditCustomerImportParam param) {
        CreditCustomer entity = new CreditCustomer();

        entity.setName(normalizeString(param.getName()));
        entity.setStatusTxt(normalizeString(param.getStatusTxt()));
        entity.setPrice(normalizeString(param.getPrice()));
        entity.setPublicDate(normalizeString(param.getPublicDate()));
        entity.setDataSource(normalizeString(param.getDataSource()));
        entity.setComments(normalizeString(param.getComments()));

        return entity;
    }

    private String normalizeString(String value) {
        if (ImportHelper.isBlank(value)) {
            return null;
        }
        return value.trim();
    }

    private int persistImportChunk(List<CreditCustomer> items,
                                   List<Integer> excelRowNumbers,
                                   int mpBatchSize,
                                   List<String> errorMessages) {
        return batchImportSupport.persistChunkWithFallback(
            items,
            excelRowNumbers,
            () -> {
                boolean ok = creditCustomerService.saveBatch(items, mpBatchSize);
                if (!ok) {
                    throw new RuntimeException("批量保存失败");
                }
                return items.size();
            },
            (rowItem, rowNumber) -> {
                try {
                    boolean saved = creditCustomerService.save(rowItem);
                    if (saved) {
                        return true;
                    }
                    if (rowNumber != null && rowNumber > 0) {
                        errorMessages.add("第" + rowNumber + "行：保存失败");
                    } else {
                        errorMessages.add("保存失败");
                    }
                    return false;
                } catch (DataIntegrityViolationException e) {
                    if (isDuplicateKey(e)) {
                        String name = rowItem != null ? rowItem.getName() : null;
                        String label = ImportHelper.isBlank(name) ? "数据" : ("客户【" + name.trim() + "】");
                        if (rowNumber != null && rowNumber > 0) {
                            errorMessages.add("第" + rowNumber + "行：" + label + "重复（唯一索引冲突）");
                        } else {
                            errorMessages.add(label + "重复（唯一索引冲突）");
                        }
                        return false;
                    }
                    throw e;
                }
            },
            errorMessages
        );
    }

    private static boolean isDuplicateKey(DataIntegrityViolationException e) {
        // Prefer structured detection (SQLState / vendor error code), fall back to message contains.
        for (Throwable t = e; t != null; t = t.getCause()) {
            if (t instanceof SQLException) {
                SQLException se = (SQLException) t;
                // MySQL: 1062 Duplicate entry; PostgreSQL/H2: SQLState 23505 unique_violation
                if (se.getErrorCode() == 1062) {
                    return true;
                }
                if ("23505".equals(se.getSQLState())) {
                    return true;
                }
            }
        }
        Throwable mostSpecificCause = e.getMostSpecificCause();
        String message = mostSpecificCause != null ? mostSpecificCause.getMessage() : e.getMessage();
        if (message == null) {
            return false;
        }
        String lower = message.toLowerCase();
        return lower.contains("duplicate") && lower.contains("key");
    }

}
