package com.yc.ship.module.ota.service.distributorselfrecharge;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.XML;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.yc.ship.framework.common.pojo.PageResult;
import com.yc.ship.framework.common.util.object.BeanUtils;
import com.yc.ship.framework.security.core.LoginUser;
import com.yc.ship.framework.tenant.core.util.TenantUtils;
import com.yc.ship.module.infra.api.config.ConfigApi;
import com.yc.ship.module.ota.api.OtaDistributorApi;
import com.yc.ship.module.ota.api.dto.DistributorSelfRechargeReqDTO;
import com.yc.ship.module.ota.controller.admin.distributor.vo.LoginTypeRespVO;
import com.yc.ship.module.ota.controller.admin.distributorselfrecharge.vo.DistributorSelfRechargePageReqVO;
import com.yc.ship.module.ota.controller.admin.distributorselfrecharge.vo.DistributorSelfRechargeSaveReqVO;
import com.yc.ship.module.ota.controller.admin.distributorselfrecharge.vo.DistributorSelfRechargeSimpleRespVO;
import com.yc.ship.module.ota.dal.dataobject.distributor.DistributorDO;
import com.yc.ship.module.ota.dal.dataobject.distributorselfrecharge.DistributorSelfRechargeDO;
import com.yc.ship.module.ota.dal.mysql.distributorselfrecharge.DistributorSelfRechargeMapper;
import com.yc.ship.module.ota.service.distributor.DistributorService;
import com.yc.ship.module.ota.service.tradelog.TradeLogService;
import com.yc.ship.module.ota.util.OtaAuthUtils;
import com.yc.ship.module.system.api.user.AdminUserApi;
import com.yc.ship.module.system.api.user.dto.AdminUserRespDTO;
import com.yc.ship.module.trade.api.dto.PayOrderReqDTO;
import com.yc.ship.module.trade.api.dto.PayOrderRespDTO;
import com.yc.ship.module.trade.api.trade.TradeApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.io.*;
import java.math.BigDecimal;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception0;


/**
 * 自助充值明细 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
@Slf4j
public class DistributorSelfRechargeServiceImpl implements DistributorSelfRechargeService {


    @Resource
    private DistributorSelfRechargeMapper distributorSelfRechargeMapper;

    @Resource
    private DistributorService distributorService;

    @Resource
    private TradeLogService tradeLogService;

    @Resource
    private OtaAuthUtils otaAuthUtils;
    @Resource
    private AdminUserApi userApi;

    @Resource
    private OtaDistributorApi otaDistributorApi;

    @Resource
    private TradeApi tradeApi;

    @Resource
    private ConfigApi configApi;


    @Override
    public DistributorSelfRechargeSimpleRespVO createDistributorSelfRecharge(DistributorSelfRechargeSaveReqVO createReqVO) {
        LoginUser loginUser = otaAuthUtils.getAgencyLoginUser();
        LoginTypeRespVO loginType = distributorService.getLoginType();
        if (loginType.getType() == 3) {
            throw exception0(11_028, "当前为平台端，不能进行自助充值删除");
        }
        AdminUserRespDTO respDTO = userApi.getUser(loginUser.getId());

        // 处理对公转账逻辑
        if (createReqVO.getPayType() == 9) {
            // 对公转账逻辑：仅保存数据，不调用支付接口
            return handlePublicTransfer(createReqVO, loginType, respDTO);
        } else {
            // 插入
            DistributorSelfRechargeDO distributorSelfRecharge = BeanUtils.toBean(createReqVO, DistributorSelfRechargeDO.class);
            Long id = IdWorker.getId(distributorSelfRecharge);
            String rechargeNo = "CZ" + id;
            distributorSelfRecharge.setId(id);
            distributorSelfRecharge.setDistributorId(loginType.getDistributorId());
            distributorSelfRecharge.setRechargeState(0);
            distributorSelfRecharge.setAccountId(respDTO.getId());
            distributorSelfRecharge.setAccountName(respDTO.getNickname());
            distributorSelfRecharge.setCreator(respDTO.getId().toString());
            distributorSelfRecharge.setTenantId(loginType.getTenantId());

            PayOrderReqDTO payOrderReqDTO = new PayOrderReqDTO();
            payOrderReqDTO.setAccountId(respDTO.getId().toString());
            payOrderReqDTO.setAccountName(respDTO.getNickname());
            payOrderReqDTO.setPayType(distributorSelfRecharge.getPayType());
            payOrderReqDTO.setPayAmount(distributorSelfRecharge.getMoney());
            payOrderReqDTO.setPaySource(101);
            payOrderReqDTO.setOrderId(id);
            payOrderReqDTO.setPayExtraType(0);
            PayOrderReqDTO.PayParams payParams = new PayOrderReqDTO.PayParams();
            payParams.setSubject(loginType.getDistributorName() + "自助充值");
            payParams.setBody(loginType.getDistributorName() + "自助充值");
            payParams.setParam(rechargeNo);
            payOrderReqDTO.setPayParams(payParams);
            PayOrderRespDTO payOrderRespDTO = tradeApi.payOuterOrder(payOrderReqDTO).getCheckedData();
            distributorSelfRecharge.setRechargeNo(payOrderRespDTO.getOrderPayId().toString());
            TenantUtils.execute(loginType.getTenantId(), () -> {
                distributorSelfRechargeMapper.insert(distributorSelfRecharge);
            });
            DistributorSelfRechargeSimpleRespVO respVO = BeanUtils.toBean(distributorSelfRecharge, DistributorSelfRechargeSimpleRespVO.class);
            respVO.setPayPlatResult(payOrderRespDTO.getPayPlatResult());
            return respVO;
        }
    }

    private DistributorSelfRechargeSimpleRespVO handlePublicTransfer(
            DistributorSelfRechargeSaveReqVO createReqVO,
            LoginTypeRespVO loginType, AdminUserRespDTO respDTO) {
        Long distributorId = loginType.getDistributorId();
        // 1. 校验分销商信息
        DistributorDO distributor = distributorService.getDistributor(loginType.getDistributorId());
        if (distributor == null || distributor.getCmbShortNo() == null) {
            throw exception0(11_029, "未能找到对应分销商或招行短号信息");
        }

        // 2. 生成对公账号
        String bankAccount = "717902731210902" + distributor.getCmbShortNo();

        // 5. 构造响应
        DistributorSelfRechargeSimpleRespVO respVO = new DistributorSelfRechargeSimpleRespVO();
        respVO.setDistributorId(distributorId);
        respVO.setBankAccount(bankAccount);
        return respVO;
    }


    @Override
    public void updateDistributorSelfRecharge(DistributorSelfRechargeSaveReqVO updateReqVO) {
        // 校验存在
        validateDistributorSelfRechargeExists(updateReqVO.getId());
        // 更新
        DistributorSelfRechargeDO updateObj = BeanUtils.toBean(updateReqVO, DistributorSelfRechargeDO.class);
        distributorSelfRechargeMapper.updateById(updateObj);
    }

    @Override
    public void deleteDistributorSelfRecharge(Long id) {
        LoginTypeRespVO loginType = distributorService.getLoginType();
        if (loginType.getType() == 3) {
            throw exception0(11_028, "当前为平台端，不能进行自助充值删除");
        }
        TenantUtils.execute(loginType.getTenantId(), () -> {
            // 校验存在
            validateDistributorSelfRechargeExists(id);
            // 删除
            distributorSelfRechargeMapper.deleteById(id);
        });
    }

    private void validateDistributorSelfRechargeExists(Long id) {
        if (distributorSelfRechargeMapper.selectById(id) == null) {
            throw exception0(11_028, "不存在");
        }
    }

    @Override
    public DistributorSelfRechargeDO getDistributorSelfRecharge(Long id) {
        LoginTypeRespVO loginType = distributorService.getLoginType();
        if (loginType.getType() == 3) {
            return distributorSelfRechargeMapper.selectById(id);
        }
        AtomicReference<DistributorSelfRechargeDO> rechargeReference = new AtomicReference<>();
        TenantUtils.execute(loginType.getTenantId(), () -> {
            rechargeReference.set(distributorSelfRechargeMapper.selectById(id));
        });
        return rechargeReference.get();
    }

    @Override
    public PageResult<DistributorSelfRechargeDO> getDistributorSelfRechargePage(DistributorSelfRechargePageReqVO pageReqVO) {
        LoginTypeRespVO loginType = distributorService.getLoginType();
        if (loginType.getType() != 3) {
            pageReqVO.setDistributorId(loginType.getDistributorId());
            AtomicReference<PageResult<DistributorSelfRechargeDO>> pageReference = new AtomicReference<>();
            TenantUtils.execute(loginType.getTenantId(), () -> {
                pageReference.set(distributorSelfRechargeMapper.selectPage(pageReqVO));
            });
            return pageReference.get();
        }
        return distributorSelfRechargeMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long distributorSelfRecharge(Long id, String payOrderId, Integer payStatus) {
        log.info("自助充值支付完成：{}，{},{}", id, payOrderId, payStatus);
        AtomicReference<Long> tradeLogId = new AtomicReference<>();
        DistributorSelfRechargeDO distributorSelfRecharge = getDistributorSelfRecharge(id);
        if (distributorSelfRecharge.getRechargeState() == 1) {
            throw exception0(11_029, "当前自助充值已处理");
        }
        TenantUtils.execute(distributorSelfRecharge.getTenantId(), () -> {
            distributorSelfRecharge.setRechargeState(payStatus == 1 ? 1 : -1);
            tradeLogId.set(tradeLogService.distributorRecharge(distributorSelfRecharge.getDistributorId(), id, 11, distributorSelfRecharge.getAccountId(), distributorSelfRecharge.getAccountName(), distributorSelfRecharge.getMoney()));
            distributorSelfRechargeMapper.updateById(distributorSelfRecharge);
        });
        return tradeLogId.get();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long distributorCMBRecharge(Long distributorId,Long id, String payOrderId, Integer payStatus) {
        log.info("对公转账充值支付完成：{}，{},{}", id, payOrderId, payStatus);
        AtomicReference<Long> tradeLogId = new AtomicReference<>();
        DistributorSelfRechargeDO distributorSelfRecharge = distributorSelfRechargeMapper.selectById(id);
//        DistributorSelfRechargeDO distributorSelfRecharge = getDistributorSelfRecharge(id);
        if (distributorSelfRecharge.getRechargeState() == 1) {
            throw exception0(11_029, "当前自助充值已处理");
        }
        TenantUtils.execute(distributorSelfRecharge.getTenantId(), () -> {
            distributorSelfRecharge.setRechargeState(payStatus == 1 ? 1 : -1);
            tradeLogId.set(tradeLogService.distributorRecharge(distributorSelfRecharge.getDistributorId(), id, 11, distributorSelfRecharge.getAccountId(), distributorSelfRecharge.getAccountName(), distributorSelfRecharge.getMoney()));
            distributorSelfRechargeMapper.updateById(distributorSelfRecharge);
        });
        return tradeLogId.get();
    }

    @Override
    public void abcRemittance(DistributorDO distributor, String host, Integer port, String yqtCard, int ofday, String url) {
        String cardNo = distributor.getCmbShortNo();
        log.error("abc对公转账充值查询{}", cardNo);

        try {
            // 1. 获取当前时间，用于组装公共包头流水与日期
            Date now = new Date();
            String reqDate = new SimpleDateFormat("yyyyMMdd").format(now);
            String reqTime = new SimpleDateFormat("HHmmss").format(now);
            // 构造请求方流水号 (保证唯一性)
            Random random = new Random();
            int randomNumber2 = random.nextInt(1000) + 1000;
            String reqSeqNo = new SimpleDateFormat("yyyyMMddHHmmss").format(now) + randomNumber2;

            // 1. 获取当前时间，用于组装公共包头流水与日期
            long s1 = System.currentTimeMillis();
            long s0 = s1 - 172800000;

            Date sDate = DateUtil.offsetDay(now, -ofday);
            String endDate = new SimpleDateFormat("yyyyMMdd").format(now);
            String startDate = new SimpleDateFormat("yyyyMMdd").format(sDate);

            // 2. 构造 CMLT40 增加多级账簿 XML 请求报文
            String xmlData = "<ap>\n" +
                    "  <CCTransCode>CQRD02</CCTransCode>\n" +
                    "  <ProductID>ICC</ProductID>\n" +
                    "  <ChannelType>ERP</ChannelType>\n" +
                    "  <CorpNo></CorpNo>\n" +
                    "  <OpNo></OpNo>\n" +
                    "  <AuthNo></AuthNo>\n" +
                    "  <ReqSeqNo>" + reqSeqNo + "</ReqSeqNo>\n" +
                    "  <ReqDate>" + reqDate + "</ReqDate>\n" +
                    "  <ReqTime>" + reqTime + "</ReqTime>\n" +
                    "  <Sign></Sign>\n" +
                    "  <Corp>\n" +
                    "    <StartDate>" + startDate + "</StartDate>\n" +
                    "    <EndDate>" + endDate + "</EndDate>\n" +
                    "  </Corp>\n" +
                    "  <Cmp>\n" +
                    "    <DbProv>17</DbProv>\n" +
                    "    <StartTime>" + s0 + "</StartTime>\n" +
                    "    <DbAccNo>" + yqtCard + "</DbAccNo>\n" +
                    "    <DbLogAccNo>" + cardNo + "</DbLogAccNo>\n" +
                    "    <DbCur>01</DbCur>\n" +
                    "    <LogAccBkOInd>0</LogAccBkOInd>\n" +
                    "  </Cmp>\n" +
                    "</ap>";

            // 3. 按照约定编码报文 (ERP上送报文必须为GBK字符集)
            byte[] xmlBytes = xmlData.getBytes("GBK");

            // 4. 构造7字节包头
            // 第1字节为加密标志（0-不加密），后6字节为数据包长度（包含加密标志位），不足6位在右边用空格补足
//            int packetLen = xmlBytes.length + 1;
            int packetLen = xmlBytes.length;
            String lengthStr = String.format("%-6s", packetLen);
            String headerStr = "0" + lengthStr;
            byte[] headerBytes = headerStr.getBytes("GBK");

            // 拼接完整的待发送字节数组
            byte[] fullPacket = new byte[headerBytes.length + xmlBytes.length];
            System.arraycopy(headerBytes, 0, fullPacket, 0, headerBytes.length);
            System.arraycopy(xmlBytes, 0, fullPacket, headerBytes.length, xmlBytes.length);

            // 5. 发送 TCP/IP Socket 同步短连接请求
            System.out.println("正在连接到前置机 " + host + ":" + port + "...");
            Socket socket = new Socket(host, port);
            // 设置读取超时时间为 30 秒
//            socket.setSoTimeout(30000);

            OutputStream out = socket.getOutputStream();
            out.write(fullPacket);
            out.flush();
            System.out.println("请求报文发送成功，等待响应...\n");
            System.out.println("请求报文发送成功，等待响应...\n" + new String(fullPacket, "GBK"));

            // 6. 接收并解析前置机返回的报文
            InputStream in = socket.getInputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            // 使用 StringBuilder 拼接读取到的报文内容，必须以 GBK 解码
            StringBuilder responseBuilder = new StringBuilder();

            while ((bytesRead = in.read(buffer)) != -1) {
                responseBuilder.append(new String(buffer, 0, bytesRead, "GBK"));
            }

            socket.close();
            in.close();
            out.close();

            String recvData = responseBuilder.toString();
            if (recvData.length() >= 7) {
                String respHeader = recvData.substring(0, 7);
                String respXml = recvData.substring(7);
                log.error("收到前置机响应头: '{}'", respHeader);
                log.error("收到前置机响应报文:\n{}", respXml);
                JSONObject jsonObject = XML.toJSONObject(respXml);

                JSONObject ap = jsonObject.getJSONObject("ap");
                String respCode = ap.getStr("RespCode");
                if (!"0000".equals(respCode)) {
                    log.error("交易发送异常，异常信息：{}", ap.getStr("RespInfo"));
                } else {
                    JSONObject cmp = ap.getJSONObject("Cmp");
                    JSONObject cme = ap.getJSONObject("Cme");
                    String batchFileName = cmp.getStr("BatchFileName");
                    String recordNum = cme.getStr("RecordNum");
                    if (StringUtils.equals("0", recordNum)) {
                        log.error("无交易数据{}", cardNo);
                        return;
                    }
                    try {
                        // 1. 创建 URL 对象
                        URL conurl = new URL(url + batchFileName);
                        // 2. 打开连接
                        URLConnection connection = conurl.openConnection();
                        // 3. 设置可选参数（例如超时时间、User-Agent）
                        connection.setConnectTimeout(5000); // 连接超时 5 秒
                        connection.setReadTimeout(5000);    // 读取超时 5 秒
                        connection.setRequestProperty("User-Agent", "Mozilla/5.0");

                        // 4. 获取输入流
                        InputStream inputStream = connection.getInputStream();

                        // 5. 读取数据
                        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "GBK"));
                        String line;
                        while ((line = reader.readLine()) != null) {
                            log.error(line);
                            //省市代码|账号|账薄号|货币码|交易日期|日志号|交易时间|户名|账薄名|交易类型|交
                            //易行号|传票号|发生额标识|现转标志|交易金额|账户余额|上笔余额|凭证种类|交易码|
                            //凭证号|对方账号省市代码|对方账号|对方账号货币码|对方账号户名|对方账号开户行|
                            //错账日期|错账传票号|记账柜员|摘要|附言|红蓝字标识|备注 1|备注 2|
                            //17|342101046484369|1203444786|01|20260423|1527670363|20260423112009578611|宜畅式东士煌本孔凰于慊官送|宜畅式东士煌本孔凰于慊官送重庆万友国际旅行社
                            // |0||3|0|1|30.00|30.00|0.00|2|2581|00000000|17|342101046420983||宜常收镐赛坭足克凰幼慊关嗣|中国农业银行股份有限公司枝江市支行||||0199|测试3||||
                            String[] split = line.split("\\|");
                            String refNbr = split[5];
                            String logTime = split[6];
                            String recardNo = split[2];
                            String trsAmt = split[14];
                            log.error("日志号：{}交易时间：{}卡号：{}金额：{}", refNbr, logTime, recardNo, trsAmt);
                            LambdaQueryWrapper<DistributorSelfRechargeDO> existCheckWrapper = new LambdaQueryWrapper<>();
                            existCheckWrapper.eq(DistributorSelfRechargeDO::getRechargeNo, refNbr);
                            if (distributorSelfRechargeMapper.selectCount(existCheckWrapper) > 0) {
                                log.warn("refNbr [{}] 已存在，跳过重复插入", refNbr);
                                return;
                            }
                            try {
                                DistributorSelfRechargeDO newRecharge = new DistributorSelfRechargeDO();
                                newRecharge.setId(IdWorker.getId());
                                newRecharge.setDistributorId(distributor.getId());
                                newRecharge.setRechargeNo(refNbr);
                                newRecharge.setMoney(new BigDecimal(trsAmt).setScale(2));
                                newRecharge.setRechargeState(0);
                                newRecharge.setPayType(9);
                                newRecharge.setAccountId(distributor.getAccountTenantId());
                                newRecharge.setAccountName(distributor.getName());
                                newRecharge.setCreator(distributor.getName());
                                newRecharge.setTenantId(distributor.getTenantId());
                                newRecharge.setCreateTime(LocalDateTime.now());
                                newRecharge.setUpdateTime(LocalDateTime.now());

                                TenantUtils.execute(distributor.getTenantId(), () -> {
                                    distributorSelfRechargeMapper.insert(newRecharge);
                                });
                                log.error("成功插入充值记录 | refNbr:{}", refNbr);
                                DistributorSelfRechargeReqDTO reqDTO = new DistributorSelfRechargeReqDTO();
                                reqDTO.setOrderId(newRecharge.getId());
                                reqDTO.setPayOrderId(refNbr);
                                reqDTO.setDistributorId(distributor.getId());
                                reqDTO.setPayStatus(1);
                                try {
                                    otaDistributorApi.distributorCMBRecharge(reqDTO).getCheckedData();
                                    log.error("充值通知成功 | orderId:{}", newRecharge.getId());
                                } catch (Exception e) {
                                    log.error("充值通知失败 | orderId:{}", newRecharge.getId(), e);
                                }
                            } catch (NumberFormatException e) {
                                log.error("金额格式错误 | trsamt:{}", trsAmt, e);
                            }
                        }

                        // 6. 关闭资源
                        reader.close();
                        inputStream.close();

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                log.error("响应报文异常或为空");
            }

        } catch (SocketTimeoutException e) {
            log.error("交易发送超时，请检查ICT前置机通讯状态。{}", String.valueOf(e));
        } catch (Exception e) {
            log.error("交易发送异常: {}", String.valueOf(e));
            e.printStackTrace();
        }

    }

}
