package com.bizzan.bitrade.service;

import com.bizzan.bitrade.dao.ConfigRobotDao;
import com.bizzan.bitrade.entity.ConfigRobot;
import com.bizzan.bitrade.service.Base.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class ConfigRobotService extends BaseService {

    private static final BigDecimal DEFAULT_HOVER_RANGE = new BigDecimal("0.0200");
    private static final BigDecimal DEFAULT_MIN_CHANGE_RATE = new BigDecimal("0.001000");
    private static final BigDecimal DEFAULT_MAX_CHANGE_RATE = new BigDecimal("0.005000");
    private static final BigDecimal DEFAULT_HOVER_MIN_CHANGE_RATE = new BigDecimal("0.000500");
    private static final BigDecimal DEFAULT_HOVER_MAX_CHANGE_RATE = new BigDecimal("0.002000");
    private static final BigDecimal DEFAULT_BRIDGE_MIN_RATIO = new BigDecimal("0.0500");
    private static final BigDecimal DEFAULT_BRIDGE_MAX_RATIO = new BigDecimal("0.1000");
    private static final BigDecimal DEFAULT_KLINE_MAX_HIGH_RATIO = new BigDecimal("0.0050");
    private static final BigDecimal DEFAULT_KLINE_MAX_LOW_RATIO = new BigDecimal("0.0050");

    @Autowired
    private ConfigRobotDao configRobotDao;

    public ConfigRobot findOne(Long id) {
        return configRobotDao.findOne(id);
    }

    public ConfigRobot findBySymbol(String symbol) {
        return configRobotDao.findBySymbol(symbol);
    }

    public List<ConfigRobot> findAll() {
        return configRobotDao.findAll();
    }

    public Page<ConfigRobot> pageQuery(String symbol, Integer status, Pageable pageable) {
        Specification<ConfigRobot> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.hasText(symbol)) {
                predicates.add(cb.equal(root.get("symbol"), symbol));
            }
            if (status != null) {
                predicates.add(cb.equal(root.get("status"), status));
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        return configRobotDao.findAll(spec, pageable);
    }

    /**
     * 校验机器人配置，与 config_robot 表 NOT NULL / 业务规则对齐
     *
     * @return 错误信息，通过时返回 null
     */
    public String validate(ConfigRobot robot) {
        if (robot == null) {
            return "配置不能为空";
        }
        if (!StringUtils.hasText(robot.getSymbol())) {
            return "交易对不能为空";
        }
        if (robot.getTrend() == null || robot.getTrend() < 1 || robot.getTrend() > 3) {
            return "趋势方向必须为 1(涨)、2(跌) 或 3(横盘)";
        }
        if (robot.getStatus() == null || robot.getStatus() < 1 || robot.getStatus() > 4) {
            return "状态必须为 1~4";
        }

        String err = requirePositive(robot.getOpenPrice(), "起始参考价");
        if (err != null) {
            return err;
        }

        err = requirePositive(robot.getHoverRange(), "盘整区间比例");
        if (err != null) {
            return err;
        }

        err = requirePositive(robot.getMinChangeRate(), "趋势最小波动率");
        if (err != null) {
            return err;
        }
        err = requirePositive(robot.getMaxChangeRate(), "趋势最大波动率");
        if (err != null) {
            return err;
        }
        err = requireMinNotGreaterThanMax(robot.getMinChangeRate(), robot.getMaxChangeRate(),
                "趋势最小波动率不能大于最大波动率");
        if (err != null) {
            return err;
        }

        err = requirePositive(robot.getHoverMinChangeRate(), "盘整最小波动率");
        if (err != null) {
            return err;
        }
        err = requirePositive(robot.getHoverMaxChangeRate(), "盘整最大波动率");
        if (err != null) {
            return err;
        }
        err = requireMinNotGreaterThanMax(robot.getHoverMinChangeRate(), robot.getHoverMaxChangeRate(),
                "盘整最小波动率不能大于最大波动率");
        if (err != null) {
            return err;
        }

        if (robot.getBridgeMinRatio() == null) {
            return "成桥最小比例不能为空";
        }
        if (robot.getBridgeMaxRatio() == null) {
            return "成桥最大比例不能为空";
        }
        if (robot.getBridgeMinRatio().compareTo(BigDecimal.ZERO) < 0
                || robot.getBridgeMaxRatio().compareTo(BigDecimal.ZERO) < 0) {
            return "成桥比例不能为负数";
        }
        err = requireMinNotGreaterThanMax(robot.getBridgeMinRatio(), robot.getBridgeMaxRatio(),
                "成桥最小比例不能大于最大比例");
        if (err != null) {
            return err;
        }

        err = requirePositive(robot.getKlineMaxHighRatio(), "K线最高价上限比例");
        if (err != null) {
            return err;
        }
        err = requirePositive(robot.getKlineMaxLowRatio(), "K线最低价下限比例");
        if (err != null) {
            return err;
        }

        if (robot.getIntervalMs() == null || robot.getIntervalMs() < 1) {
            return "执行间隔不能小于 1ms";
        }

        if (robot.getTargetPrice() != null && robot.getTargetPrice().compareTo(BigDecimal.ZERO) <= 0) {
            return "目标价格必须大于 0";
        }
        if (robot.getUpProbability() != null) {
            if (robot.getUpProbability().compareTo(BigDecimal.ZERO) < 0
                    || robot.getUpProbability().compareTo(BigDecimal.ONE) > 0) {
                return "上涨概率必须在 0~1 之间";
            }
        }
        if (robot.getMinuteChangeLimit() != null && robot.getMinuteChangeLimit().compareTo(BigDecimal.ZERO) <= 0) {
            return "分钟涨跌幅限制必须大于 0";
        }
        if (robot.getRemark() != null && robot.getRemark().length() > 200) {
            return "备注不能超过 200 字";
        }
        return null;
    }

    /** 写入表默认值（仅对 NOT NULL 且未传值的字段） */
    public void applyDefaults(ConfigRobot robot) {
        if (robot.getTrend() == null) {
            robot.setTrend(1);
        }
        if (robot.getHoverRange() == null) {
            robot.setHoverRange(DEFAULT_HOVER_RANGE);
        }
        if (robot.getMinChangeRate() == null) {
            robot.setMinChangeRate(DEFAULT_MIN_CHANGE_RATE);
        }
        if (robot.getMaxChangeRate() == null) {
            robot.setMaxChangeRate(DEFAULT_MAX_CHANGE_RATE);
        }
        if (robot.getHoverMinChangeRate() == null) {
            robot.setHoverMinChangeRate(DEFAULT_HOVER_MIN_CHANGE_RATE);
        }
        if (robot.getHoverMaxChangeRate() == null) {
            robot.setHoverMaxChangeRate(DEFAULT_HOVER_MAX_CHANGE_RATE);
        }
        if (robot.getBridgeMinRatio() == null) {
            robot.setBridgeMinRatio(DEFAULT_BRIDGE_MIN_RATIO);
        }
        if (robot.getBridgeMaxRatio() == null) {
            robot.setBridgeMaxRatio(DEFAULT_BRIDGE_MAX_RATIO);
        }
        if (robot.getIntervalMs() == null) {
            robot.setIntervalMs(3000);
        }
        if (robot.getStatus() == null) {
            robot.setStatus(1);
        }
        if (robot.getKlineMaxHighRatio() == null) {
            robot.setKlineMaxHighRatio(DEFAULT_KLINE_MAX_HIGH_RATIO);
        }
        if (robot.getKlineMaxLowRatio() == null) {
            robot.setKlineMaxLowRatio(DEFAULT_KLINE_MAX_LOW_RATIO);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public ConfigRobot create(ConfigRobot robot) {
        applyDefaults(robot);
        robot.setCreateTime(new Date());
        robot.setUpdateTime(new Date());
        return configRobotDao.save(robot);
    }

    @Transactional(rollbackFor = Exception.class)
    public ConfigRobot update(ConfigRobot robot) {
        robot.setUpdateTime(new Date());
        return configRobotDao.save(robot);
    }

    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        configRobotDao.delete(id);
    }

    private static String requirePositive(BigDecimal value, String label) {
        if (value == null) {
            return label + "不能为空";
        }
        if (value.compareTo(BigDecimal.ZERO) <= 0) {
            return label + "必须大于 0";
        }
        return null;
    }

    private static String requireMinNotGreaterThanMax(BigDecimal min, BigDecimal max, String message) {
        if (min != null && max != null && min.compareTo(max) > 0) {
            return message;
        }
        return null;
    }
}
