package com.ruoyi.web.sales.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.web.base.domain.BaseEmployee;
import com.ruoyi.web.base.domain.BaseMaterial;
import com.ruoyi.web.base.service.IBaseEmployeeService;
import com.ruoyi.web.base.service.IBaseMaterialService;
import com.ruoyi.web.sales.domain.BaseCustomer;
import com.ruoyi.web.sales.domain.SalesDelivery;
import com.ruoyi.web.sales.domain.SalesDeliveryGoods;
import com.ruoyi.web.sales.service.IBaseCustomerService;
import com.ruoyi.web.sales.service.ISalesDeliveryGoodsService;
import com.ruoyi.web.sales.service.ISalesDeliveryService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.ruoyi.common.core.domain.AjaxResult.success;
import static com.ruoyi.common.utils.SecurityUtils.getUsername;

@RestController
@Api(tags = "销售出库单")
@RequestMapping("/sales-delivery")
public class SalesDeliveryController {

    @Autowired
    private ISalesDeliveryService salesDeliveryService;
    @Autowired
    private ISalesDeliveryGoodsService salesDeliveryGoodsService;
    @Autowired
    private IBaseCustomerService customerService;
    @Autowired
    private IBaseEmployeeService baseEmployeeService;
    @Autowired
    private IBaseMaterialService baseMaterialService;
    @Autowired
    private TokenService tokenService;

    @ApiOperation("销售出库单新增")
    @PostMapping("/add")
    @Transactional
    public AjaxResult add(@RequestBody SalesDelivery delivery, HttpServletRequest request) throws Exception {
        String orgId = tokenService.getLoginOrgId(request);
        validateBeforeSave(delivery, orgId, true);
        fillCustomerInfo(delivery, orgId);
        recalcGoodsAmountAndTotal(delivery);
        delivery.setOrgId(orgId);
        delivery.setDelFlag("0");
        delivery.setAuditStatus(1);
        if (delivery.getOffsetStatus() == null) {
            delivery.setOffsetStatus(0);
        }
        String username = getUsername();
        delivery.setId(null);
        delivery.setCreateBy(username);
        delivery.setCreateTime(new Date());
        delivery.setUpdateBy(null);
        delivery.setUpdateTime(null);
        salesDeliveryService.save(delivery);
        saveGoods(delivery, orgId, username);
        return success();
    }

    @ApiOperation("销售出库单修改")
    @PostMapping("/edit")
    @Transactional
    public AjaxResult edit(@RequestBody SalesDelivery delivery, HttpServletRequest request) throws Exception {
        if (delivery.getId() == null) {
            throw new Exception("id不能为空");
        }
        SalesDelivery old = salesDeliveryService.getById(delivery.getId());
        if (old == null || !"0".equals(old.getDelFlag())) {
            throw new Exception("销售出库单不存在");
        }
        if (old.getAuditStatus() != null && old.getAuditStatus() == 1) {
            throw new Exception("已审核的销售出库单不能修改，请先反审核");
        }
        String orgId = tokenService.getLoginOrgId(request);
        validateBeforeSave(delivery, orgId, false);
        fillCustomerInfo(delivery, orgId);
        recalcGoodsAmountAndTotal(delivery);
        delivery.setOrgId(orgId);
        delivery.setAuditStatus(1);
        delivery.setOffsetStatus(old.getOffsetStatus());
        String username = getUsername();
        delivery.setCreateBy(old.getCreateBy());
        delivery.setCreateTime(old.getCreateTime());
        delivery.setUpdateBy(username);
        delivery.setUpdateTime(new Date());
        salesDeliveryService.updateById(delivery);
        String oldNum = StringUtils.isNotBlank(old.getDeliveryNum()) ? old.getDeliveryNum().trim() : null;
        deleteGoodsByDeliveryNum(orgId, oldNum);
        saveGoods(delivery, orgId, username);
        return success();
    }

    @ApiOperation("销售出库单删除")
    @PostMapping("/delete")
    public AjaxResult delete(@RequestBody Map<String, String> params) throws Exception {
        String ids = params.get("ids");
        for (String id : ids.split(",")) {
            SalesDelivery delivery = salesDeliveryService.getById(id);
            if (delivery == null || !"0".equals(delivery.getDelFlag())) {
                continue;
            }
            if (delivery.getAuditStatus() != null && delivery.getAuditStatus() == 1) {
                throw new Exception("已审核的销售出库单不能删除，请先反审核");
            }
            deleteGoodsByDeliveryNum(delivery.getOrgId(), delivery.getDeliveryNum());
            SalesDelivery up = new SalesDelivery();
            up.setId(Integer.valueOf(id));
            up.setDelFlag("2");
            up.setUpdateBy(getUsername());
            up.setUpdateTime(new Date());
            salesDeliveryService.updateById(up);
        }
        return success();
    }

    @ApiOperation("销售出库单详情")
    @PostMapping("/listById")
    public AjaxResult listById(@ApiParam("详情参数: id") @RequestBody Map<String, String> params, HttpServletRequest request) {
        SalesDelivery delivery = salesDeliveryService.getById(params.get("id"));
        if (delivery == null) {
            return success(null);
        }
        String orgId = tokenService.getLoginOrgId(request);
        List<SalesDeliveryGoods> goods = salesDeliveryGoodsService.list(new QueryWrapper<SalesDeliveryGoods>()
                .eq("org_id", orgId)
                .eq("delivery_num", delivery.getDeliveryNum())
                .eq("del_flag", "0"));
        enrichGoodsDisplay(goods, orgId);
        enrichDeliveryDisplay(delivery, orgId);
        delivery.setGoods(goods);
        return success(delivery);
    }

    @ApiOperation("销售出库单分页")
    @GetMapping("/page")
    public AjaxResult page(@RequestParam("pageNum") Integer pageNum,
                           @RequestParam("pageSize") Integer pageSize,
                           SalesDelivery query,
                           @RequestParam(value = "goodsNum", required = false) String goodsNum,
                           HttpServletRequest request) {
        String orgId = tokenService.getLoginOrgId(request);
        QueryWrapper<SalesDelivery> wrapper = buildWrapper(query, orgId, goodsNum);
        Page<SalesDelivery> page = salesDeliveryService.page(new Page<SalesDelivery>(pageNum, pageSize), wrapper);
        for (SalesDelivery row : page.getRecords()) {
            enrichDeliveryDisplay(row, orgId);
        }
        return success(page);
    }

    @ApiOperation("销售出库单列表")
    @PostMapping("/list")
    public AjaxResult list(@RequestBody SalesDelivery query, HttpServletRequest request) {
        String orgId = tokenService.getLoginOrgId(request);
        String goodsNum = query == null ? null : query.getGoodsNum();
        List<SalesDelivery> list = salesDeliveryService.list(buildWrapper(query, orgId, goodsNum));
        for (SalesDelivery row : list) {
            enrichDeliveryDisplay(row, orgId);
        }
        return success(list);
    }

    @ApiOperation("销售出库单导出")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SalesDelivery query, HttpServletRequest request) {
        String orgId = tokenService.getLoginOrgId(request);
        List<SalesDelivery> list = salesDeliveryService.list(buildWrapper(query, orgId, null));
        for (SalesDelivery row : list) {
            enrichDeliveryDisplay(row, orgId);
        }
        ExcelUtil<SalesDelivery> util = new ExcelUtil<SalesDelivery>(SalesDelivery.class);
        util.exportExcel(response, list, "销售出库单");
    }

    @ApiOperation("销售出库单审核")
    @PostMapping("/audit")
    public AjaxResult audit(@RequestBody Map<String, String> params) throws Exception {
        SalesDelivery delivery = getActiveById(params.get("id"));
        delivery.setAuditStatus(1);
        delivery.setUpdateBy(getUsername());
        delivery.setUpdateTime(new Date());
        return success(salesDeliveryService.updateById(delivery));
    }

    @ApiOperation("销售出库单反审核")
    @PostMapping("/unaudit")
    public AjaxResult unaudit(@RequestBody Map<String, String> params) throws Exception {
        SalesDelivery delivery = getActiveById(params.get("id"));
        if (delivery.getOffsetStatus() != null && delivery.getOffsetStatus() == 1) {
            throw new Exception("已做冲销的销售出库单不可反审核！请先反冲销并删除相应现金收款！");
        }
        delivery.setAuditStatus(0);
        delivery.setUpdateBy(getUsername());
        delivery.setUpdateTime(new Date());
        return success(salesDeliveryService.updateById(delivery));
    }

    @ApiOperation("销售出库单打印数据")
    @PostMapping("/print")
    public AjaxResult print(@RequestBody Map<String, String> params, HttpServletRequest request) throws Exception {
        SalesDelivery delivery = getActiveById(params.get("id"));
        String orgId = tokenService.getLoginOrgId(request);
        List<SalesDeliveryGoods> goods = salesDeliveryGoodsService.list(new QueryWrapper<SalesDeliveryGoods>()
                .eq("org_id", orgId)
                .eq("delivery_num", delivery.getDeliveryNum())
                .eq("del_flag", "0"));
        enrichGoodsDisplay(goods, orgId);
        enrichDeliveryDisplay(delivery, orgId);
        delivery.setGoods(goods);
        return success(delivery);
    }

    @ApiOperation("获取新增销售出库单页面数据")
    @PostMapping("/getAddPageData")
    public AjaxResult getAddPageData(HttpServletRequest request) throws Exception {
        String orgId = tokenService.getLoginOrgId(request);
        String tomorrow = LocalDate.now().plusDays(1).format(DateTimeFormatter.ISO_LOCAL_DATE);
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("documentDate", tomorrow);
        data.put("deliveryNum", generateSalesDeliveryNum(orgId));
        return success(data);
    }

    private QueryWrapper<SalesDelivery> buildWrapper(SalesDelivery query, String orgId, String goodsNum) {
        QueryWrapper<SalesDelivery> wrapper = new QueryWrapper<SalesDelivery>()
                .eq("org_id", orgId).eq("del_flag", "0");
        if (query == null) return wrapper.orderByDesc("id");
        if (query.getDocumentDateStart() != null) wrapper.ge("document_date", query.getDocumentDateStart());
        if (query.getDocumentDateEnd() != null) {
            // 兼容 document_date 为 datetime 的场景：结束日按“次日零点前”筛选，避免漏掉结束日当天数据
            Calendar c = Calendar.getInstance();
            c.setTime(query.getDocumentDateEnd());
            c.add(Calendar.DAY_OF_MONTH, 1);
            wrapper.lt("document_date", c.getTime());
        }
        if (StringUtils.isNotBlank(query.getDeliveryNum())) wrapper.like("delivery_num", query.getDeliveryNum());
        if (StringUtils.isNotBlank(query.getCustomerNum())) wrapper.like("customer_num", query.getCustomerNum());
        if (StringUtils.isNotBlank(query.getCustomerName())) wrapper.like("customer_name", query.getCustomerName());
        if (StringUtils.isNotBlank(query.getEmployeeNum())) wrapper.like("employee_num", query.getEmployeeNum());
        if (query.getAuditStatus() != null) wrapper.eq("audit_status", query.getAuditStatus());
        if (StringUtils.isNotBlank(goodsNum)) {
            String safe = goodsNum.trim().replace("'", "''");
            wrapper.inSql("delivery_num", "SELECT delivery_num FROM sales_delivery_goods WHERE org_id='" + orgId + "' AND del_flag='0' AND goods_num LIKE '%" + safe + "%'");
        }
        return wrapper.orderByDesc("id");
    }

    private void validateBeforeSave(SalesDelivery delivery, String orgId, boolean isAdd) throws Exception {
        if (delivery == null) throw new Exception("参数不能为空");
        if (StringUtils.isAnyBlank(delivery.getDeliveryNum(), delivery.getCustomerNum())) throw new Exception("单据编号、客户编号不能为空");
        QueryWrapper<SalesDelivery> numWrapper = new QueryWrapper<SalesDelivery>()
                .eq("org_id", orgId).eq("delivery_num", delivery.getDeliveryNum()).eq("del_flag", "0");
        if (!isAdd && delivery.getId() != null) numWrapper.ne("id", delivery.getId());
        if (salesDeliveryService.count(numWrapper) > 0) throw new Exception("该单据编号已存在");
        if (delivery.getGoods() == null || delivery.getGoods().isEmpty()) throw new Exception("货品清单不能为空");
        if (StringUtils.isNotBlank(delivery.getEmployeeNum())) {
            BaseEmployee emp = baseEmployeeService.getOne(new QueryWrapper<BaseEmployee>()
                    .eq("org_id", orgId)
                    .eq("employee_num", delivery.getEmployeeNum().trim())
                    .eq("del_flag", "0")
                    .last("limit 1"));
            if (emp == null) throw new Exception("业务员编号不存在");
        }
    }

    private void fillCustomerInfo(SalesDelivery delivery, String orgId) throws Exception {
        BaseCustomer customer = customerService.getOne(new QueryWrapper<BaseCustomer>()
                .eq("org_id", orgId).eq("customer_num", delivery.getCustomerNum()).eq("del_flag", "0").last("limit 1"));
        if (customer == null) throw new Exception("客户编号不存在");
        delivery.setCustomerName(customer.getCustomerName());
    }

    private void recalcGoodsAmountAndTotal(SalesDelivery delivery) {
        List<SalesDeliveryGoods> goods = delivery.getGoods();
        if (goods == null || goods.isEmpty()) {
            delivery.setTotalAmount(null);
            return;
        }
        BigDecimal sum = BigDecimal.ZERO;
        boolean any = false;
        for (SalesDeliveryGoods g : goods) {
            BigDecimal p = parseMoney(g.getUnitPrice());
            BigDecimal q = parseMoney(g.getBaseNum());
            BigDecimal line = (p != null && q != null) ? p.multiply(q) : parseMoney(g.getAmount());
            if (line != null) {
                g.setAmount(formatMoney(line));
                sum = sum.add(line);
                any = true;
            } else {
                g.setAmount(null);
            }
        }
        delivery.setTotalAmount(any ? formatMoney(sum) : null);
    }

    private void saveGoods(SalesDelivery delivery, String orgId, String username) {
        List<SalesDeliveryGoods> goods = delivery.getGoods();
        if (goods == null) goods = Collections.emptyList();
        Date now = new Date();
        for (SalesDeliveryGoods g : goods) {
            g.setId(null);
            g.setOrgId(orgId);
            g.setDeliveryNum(delivery.getDeliveryNum());
            g.setDelFlag("0");
            g.setCreateBy(username);
            g.setCreateTime(now);
            g.setUpdateBy(null);
            g.setUpdateTime(null);
            salesDeliveryGoodsService.save(g);
        }
    }

    private void enrichGoodsDisplay(List<SalesDeliveryGoods> goods, String orgId) {
        if (goods == null || goods.isEmpty()) return;
        for (SalesDeliveryGoods g : goods) {
            if (StringUtils.isBlank(g.getGoodsNum())) continue;
            BaseMaterial m = baseMaterialService.getOne(new QueryWrapper<BaseMaterial>()
                    .eq("org_id", orgId).eq("goods_num", g.getGoodsNum()).eq("del_flag", "0").last("limit 1"));
            if (m == null) continue;
            g.setGoodsName(m.getGoodsName());
            if (StringUtils.isBlank(g.getGoodsSpec())) g.setGoodsSpec(m.getGoodsSpec());
            if (StringUtils.isBlank(g.getBaseUnit())) g.setBaseUnit(m.getUnit());
            if (StringUtils.isBlank(g.getAssUnit())) g.setAssUnit(m.getAssistantUnit());
            if (StringUtils.isBlank(g.getWarehouseName())) g.setWarehouseName(m.getWarehouseName());
        }
    }

    private void enrichDeliveryDisplay(SalesDelivery delivery, String orgId) {
        if (delivery == null) return;
        delivery.setEmployeeName(null);
        if (StringUtils.isNotBlank(delivery.getEmployeeNum())) {
            BaseEmployee e = baseEmployeeService.getOne(new QueryWrapper<BaseEmployee>()
                    .eq("org_id", orgId)
                    .eq("employee_num", delivery.getEmployeeNum().trim())
                    .eq("del_flag", "0")
                    .last("limit 1"));
            if (e != null) {
                delivery.setEmployeeName(e.getEmployeeName());
            }
        }
    }

    private void deleteGoodsByDeliveryNum(String orgId, String deliveryNum) {
        if (StringUtils.isBlank(orgId) || StringUtils.isBlank(deliveryNum)) return;
        SalesDeliveryGoods up = new SalesDeliveryGoods();
        up.setDelFlag("2");
        up.setUpdateBy(getUsername());
        up.setUpdateTime(new Date());
        salesDeliveryGoodsService.update(up, new QueryWrapper<SalesDeliveryGoods>()
                .eq("org_id", orgId)
                .eq("delivery_num", deliveryNum)
                .eq("del_flag", "0"));
    }

    private BigDecimal parseMoney(String s) {
        if (StringUtils.isBlank(s)) return null;
        try {
            return new BigDecimal(s.trim().replace(",", ""));
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 单据编号规则：
     * XSCK + 当前日期后一天的yyyyMMdd + 5位递增序号（不足补零，从00001开始）
     */
    private String generateSalesDeliveryNum(String orgId) throws Exception {
        if (StringUtils.isBlank(orgId)) {
            throw new Exception("orgId不能为空");
        }
        String tomorrowYmd = LocalDate.now().plusDays(1).format(DateTimeFormatter.BASIC_ISO_DATE);
        String base = "XSCK" + tomorrowYmd;

        SalesDelivery last = salesDeliveryService.getOne(new QueryWrapper<SalesDelivery>()
                .eq("org_id", orgId)
                .eq("del_flag", "0")
                .like("delivery_num", base)
                .orderByDesc("delivery_num")
                .last("limit 1"));

        int startSeq = 1;
        if (last != null && StringUtils.isNotBlank(last.getDeliveryNum()) && last.getDeliveryNum().length() >= 5) {
            String suffix = last.getDeliveryNum().substring(last.getDeliveryNum().length() - 5);
            try {
                startSeq = Integer.parseInt(suffix) + 1;
            } catch (Exception ignored) {
                startSeq = 1;
            }
        }

        for (int seq = startSeq; seq <= 99999; seq++) {
            String candidate = base + String.format("%05d", seq);
            long occupied = salesDeliveryService.count(new QueryWrapper<SalesDelivery>()
                    .eq("org_id", orgId)
                    .eq("delivery_num", candidate)
                    .eq("del_flag", "0"));
            if (occupied == 0) {
                return candidate;
            }
        }
        throw new Exception("无法生成唯一销售出库单编号");
    }

    private String formatMoney(BigDecimal n) {
        if (n == null) return null;
        return n.stripTrailingZeros().toPlainString();
    }

    private SalesDelivery getActiveById(String id) throws Exception {
        SalesDelivery delivery = salesDeliveryService.getById(id);
        if (delivery == null || !"0".equals(delivery.getDelFlag())) {
            throw new Exception("销售出库单不存在");
        }
        return delivery;
    }
}
