package com.bizzan.bitrade.controller.documentary;

import com.bizzan.bitrade.controller.common.BaseAdminController;
import com.bizzan.bitrade.entity.Admin;
import com.bizzan.bitrade.service.AdminService;
import com.bizzan.bitrade.util.MessageResult;
import com.sparkframework.security.Encrypt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.Collections;

@Slf4j
@RestController
@RequestMapping("/copy-trade-position")
public class CopyTradePositionController extends BaseAdminController {

    @Resource
    private JdbcTemplate jdbcTemplate;

    @Resource
    @Qualifier("plainRestTemplate")
    private RestTemplate plainRestTemplate;

    /** Eureka/Ribbon 负载均衡 RestTemplate，用于访问 contract-swap-api 等内部服务 */
    @Autowired
    private RestTemplate restTemplate;

    @Value("${datacenter.host}")
    private String datacenterHost;

    @Autowired
    private AdminService adminService;

    @Value("${spark.system.md5.key}")
    private String md5Key;

    /**
     * 分页查询跟单持仓快照。
     * POST /copy-trade-position/page-query
     */
    @PostMapping("page-query")
    public MessageResult pageQuery(
            @RequestParam(value = "pageNo",         defaultValue = "1")   int pageNo,
            @RequestParam(value = "pageSize",       defaultValue = "20")  int pageSize,
            @RequestParam(value = "traderMemberId", required = false) Long   traderMemberId,
            @RequestParam(value = "traderLevel",    required = false) String traderLevel,
            @RequestParam(value = "symbol",         required = false) String symbol,
            @RequestParam(value = "direction",      required = false) Integer direction,
            @RequestParam(value = "profitStatus",   required = false) String profitStatus) {

        StringBuilder where = new StringBuilder("WHERE 1=1");
        List<Object> params = new ArrayList<>();

        if (traderMemberId != null) {
            where.append(" AND trader_member_id = ?");
            params.add(traderMemberId);
        }
        if (traderLevel != null && !traderLevel.isEmpty()) {
            where.append(" AND trader_level = ?");
            params.add(traderLevel);
        }
        if (symbol != null && !symbol.isEmpty()) {
            where.append(" AND symbol = ?");
            params.add(symbol);
        }
        if (direction != null) {
            where.append(" AND direction = ?");
            params.add(direction);
        }
        if ("positive".equals(profitStatus)) {
            where.append(" AND trader_unrealized_pnl > 0");
        } else if ("negative".equals(profitStatus)) {
            where.append(" AND trader_unrealized_pnl < 0");
        }

        String countSql = "SELECT COUNT(*) FROM stat_copy_trade_position " + where;
        int total = jdbcTemplate.queryForObject(countSql, params.toArray(), Integer.class);

        int offset = (pageNo - 1) * pageSize;
        String dataSql = "SELECT * FROM stat_copy_trade_position " + where +
                " ORDER BY trader_member_id LIMIT ? OFFSET ?";
        List<Object> dataParams = new ArrayList<>(params);
        dataParams.add(pageSize);
        dataParams.add(offset);

        List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataSql, dataParams.toArray());
        rows.forEach(row -> {
            for (String col : new String[]{
                    "leverage_rate", "trader_pos_usdt", "trader_unrealized_pnl", "profit_rate",
                    "normal_follow_pos_usdt", "normal_follow_pnl",
                    "special_follow_pos_usdt", "special_follow_pnl"}) {
                Object v = row.get(col);
                if (v instanceof BigDecimal) row.put(col, ((BigDecimal) v).doubleValue());
            }
            for (String col : new String[]{"stat_time", "updated_time"}) {
                Object t = row.get(col);
                if (t != null) row.put(col, t.toString());
            }
            // tinyint(1) comes back as Boolean from JDBC; normalise to int
            Object dir = row.get("direction");
            if (dir instanceof Boolean) row.put("direction", ((Boolean) dir) ? 1 : 0);
        });

        // 最后快照时间
        Object lastTime = jdbcTemplate.queryForObject(
                "SELECT MAX(stat_time) FROM stat_copy_trade_position", Object.class);

        Map<String, Object> result = new HashMap<>();
        result.put("content", rows);
        result.put("totalElements", total);
        result.put("pageNo", pageNo);
        result.put("pageSize", pageSize);
        result.put("lastStatTime", lastTime != null ? lastTime.toString() : null);
        return MessageResult.getSuccessInstance("SUCCESS", result);
    }

    /**
     * 刷新快照：先从 contract-swap-api 拿实时价格，连同价格一起 POST 给 datacenter job。
     * POST /copy-trade-position/refresh
     */
    @PostMapping("refresh")
    public MessageResult refresh() {
        // 1. 通过 Eureka 负载均衡的 restTemplate 拿实时价格
        Map<String, BigDecimal> prices = Collections.emptyMap();
        try {
            String priceUrl = "http://contract-swap-api/swap/symbol-price";
            ParameterizedTypeReference<Map<String, BigDecimal>> typeRef =
                    new ParameterizedTypeReference<Map<String, BigDecimal>>() {};
            ResponseEntity<Map<String, BigDecimal>> priceResp =
                    restTemplate.exchange(priceUrl, HttpMethod.POST, new HttpEntity<>(null), typeRef);
            if (priceResp.getBody() != null && !priceResp.getBody().isEmpty()) {
                prices = priceResp.getBody();
                log.info("[CopyTradePosition] 获取到 {} 个交易对价格", prices.size());
            }
        } catch (Exception e) {
            log.warn("[CopyTradePosition] 获取实时价格失败，将由 datacenter 自行尝试: {}", e.getMessage());
        }

        // 2. 把价格随 POST body 一起转发给 datacenter，datacenter 直接使用无需再请求
        try {
            String url = datacenterHost + "/job/run/copy-trade-position";
            log.info("[CopyTradePosition] 触发 datacenter 快照任务: {}", url);
            HttpEntity<Map<String, BigDecimal>> req = new HttpEntity<>(prices);
            plainRestTemplate.postForObject(url, req, String.class);
        } catch (Exception e) {
            log.error("[CopyTradePosition] 触发 datacenter 任务失败", e);
            return error("刷新失败：" + e.getMessage());
        }
        return success("刷新成功");
    }

    /**
     * 查询跟单明细：指定带单者+交易对下的所有活跃跟单仓位。
     * POST /copy-trade-position/detail
     */
    @PostMapping("detail")
    public MessageResult detail(
            @RequestParam("traderMemberId") Long   traderMemberId,
            @RequestParam("symbol")         String symbol,
            @RequestParam(value = "memberKeyword", required = false) String memberKeyword,
            @RequestParam(value = "pageNo",   defaultValue = "1")  int pageNo,
            @RequestParam(value = "pageSize", defaultValue = "20") int pageSize) {

        // 先拿 contract_id（contract_coin 行数少，走索引快）
        List<Map<String, Object>> coinRows = jdbcTemplate.queryForList(
                "SELECT id FROM contract_coin WHERE symbol = ? LIMIT 1",
                symbol);
        if (coinRows.isEmpty()) {
            return emptyPage(pageNo, pageSize);
        }
        Long contractId = ((Number) coinRows.get(0).get("id")).longValue();

        // 先取带单者活跃仓位 ID（通常只有极少条），空则直接返回，避免扫大表
        List<Long> traderPosIds = jdbcTemplate.queryForList(
                "SELECT id FROM member_contract_position" +
                " WHERE member_id = ? AND contract_id = ? AND status != 2 AND follow_position_id IS NULL",
                Long.class, traderMemberId, contractId);
        if (traderPosIds.isEmpty()) {
            return emptyPage(pageNo, pageSize);
        }

        // money_type 取自带单者任一仓位（effectively final 供 lambda 使用）
        final boolean isCoin = resolveIsCoin(contractId, traderMemberId);

        // 用 JOIN 替代 IN 子查询，MySQL 对 JOIN 的执行计划更优
        String joinClause =
                "FROM member_contract_position fp" +
                " INNER JOIN member_contract_position tp" +
                "   ON tp.id = fp.follow_position_id" +
                "   AND tp.member_id = ?" +
                "   AND tp.contract_id = ?" +
                "   AND tp.status != 2" +
                "   AND tp.follow_position_id IS NULL";
        StringBuilder where = new StringBuilder("WHERE fp.status != 2 AND fp.contract_id = ?");
        List<Object> joinParams = new ArrayList<>(Arrays.asList(traderMemberId, contractId));
        List<Object> whereParams = new ArrayList<>(Arrays.asList(contractId));

        if (memberKeyword != null && !memberKeyword.trim().isEmpty()) {
            String kw = memberKeyword.trim();
            try {
                long uid = Long.parseLong(kw);
                where.append(" AND fp.member_id = ?");
                whereParams.add(uid);
            } catch (NumberFormatException e) {
                where.append(" AND fp.member_id IN (SELECT id FROM member WHERE email = ?)");
                whereParams.add(kw);
            }
        }

        List<Object> allParams = new ArrayList<>();
        allParams.addAll(joinParams);
        allParams.addAll(whereParams);

        String countSql = "SELECT COUNT(*) " + joinClause + " " + where;
        int total = jdbcTemplate.queryForObject(countSql, allParams.toArray(), Integer.class);

        int offset = (pageNo - 1) * pageSize;
        String dataSql =
                "SELECT fp.id, fp.member_id, ? AS symbol, fp.direction, fp.leverage," +
                "  fp.total_position - COALESCE(fp.closed_position,0) AS qty," +
                "  fp.share_number, fp.open_price, COALESCE(fp.total_principal_amount,0) AS principal " +
                joinClause + " " + where +
                " ORDER BY fp.member_id LIMIT ? OFFSET ?";

        List<Object> dataParams = new ArrayList<>();
        dataParams.add(symbol);
        dataParams.addAll(allParams);
        dataParams.add(pageSize);
        dataParams.add(offset);

        List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataSql, dataParams.toArray());

        // 获取实时价格计算未实现盈亏
        BigDecimal curPrice = fetchCurrentPrice(symbol);

        rows.forEach(row -> {
            BigDecimal qty          = toBD(row.get("qty"));
            BigDecimal shareNum     = toBD(row.get("share_number"));
            BigDecimal openPr       = toBD(row.get("open_price"));
            BigDecimal principal    = toBD(row.get("principal"));
            int dir                 = row.get("direction") instanceof Number
                                      ? ((Number) row.get("direction")).intValue() : 0;

            // COIN type: qty is already in base coin; USDT type: qty is contracts, coin_qty = qty * shareNum
            BigDecimal coinQty = isCoin ? qty : qty.multiply(shareNum);

            // COIN:  pnl = priceDiff * qty
            // USDT:  pnl = priceDiff * qty * shareNum / openPrice
            BigDecimal pnl = BigDecimal.ZERO;
            if (openPr.compareTo(BigDecimal.ZERO) > 0 && curPrice.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal priceDiff = curPrice.subtract(openPr);
                BigDecimal raw = isCoin
                        ? priceDiff.multiply(qty)
                        : priceDiff.multiply(qty).multiply(shareNum).divide(openPr, 4, RoundingMode.HALF_UP);
                pnl = dir == 1 ? raw.negate() : raw;
            }

            BigDecimal rate = BigDecimal.ZERO;
            if (principal.compareTo(BigDecimal.ZERO) > 0) {
                rate = pnl.multiply(BigDecimal.valueOf(100))
                          .divide(principal, 2, RoundingMode.HALF_UP);
            }

            row.put("coin_qty",       coinQty.setScale(4, RoundingMode.HALF_UP).doubleValue());
            row.put("unrealized_pnl", pnl.setScale(2, RoundingMode.HALF_UP).doubleValue());
            row.put("profit_rate",    rate.doubleValue());
            // convert remaining BigDecimal fields
            for (String col : new String[]{"qty","share_number","open_price","principal"}) {
                Object v = row.get(col);
                if (v instanceof BigDecimal) row.put(col, ((BigDecimal) v).doubleValue());
            }
            // tinyint(1) comes back as Boolean from JDBC; normalise to int
            Object dirVal = row.get("direction");
            if (dirVal instanceof Boolean) row.put("direction", ((Boolean) dirVal) ? 1 : 0);
        });

        Map<String, Object> result = new HashMap<>();
        result.put("content", rows);
        result.put("totalElements", total);
        result.put("pageNo", pageNo);
        result.put("pageSize", pageSize);
        return MessageResult.getSuccessInstance("SUCCESS", result);
    }

    /**
     * 强平单个跟单仓位。
     * POST /copy-trade-position/force-close-one
     */
    @PostMapping("force-close-one")
    public MessageResult forceCloseOne(
            @RequestParam("positionId") Long   positionId,
            @RequestParam("username")   String username,
            @RequestParam("password")   String password) {

        String encPwd = Encrypt.MD5(password + md5Key);
        Admin admin = adminService.login(username, encPwd);
        if (admin == null) {
            return error("操作失败，密码错误");
        }

        restTemplate.postForObject(
            "http://contract-swap-api/swap/admin/force-close-position?positionId=" + positionId,
            null, String.class);

        log.info("[CopyTradePosition] 单笔强平 positionId={} by={}", positionId, username);
        return success("操作成功");
    }

    /**
     * 一键强平指定带单者在指定交易对上的所有活跃仓位（含跟单仓位）。
     * POST /copy-trade-position/force-close
     */
    @PostMapping("force-close")
    public MessageResult forceClose(
            @RequestParam("traderMemberId") Long   traderMemberId,
            @RequestParam("symbol")         String symbol,
            @RequestParam("username")       String username,
            @RequestParam("password")       String password) {

        String encPwd = Encrypt.MD5(password + md5Key);
        Admin admin = adminService.login(username, encPwd);
        if (admin == null) {
            return error("操作失败，密码错误");
        }

        // 用 JDBC 查 contract_id，避免 JPA 懒加载 coin 关联导致过滤失效
        List<Map<String, Object>> coinRows = jdbcTemplate.queryForList(
                "SELECT id FROM contract_coin WHERE symbol = ? LIMIT 1", symbol);
        if (coinRows.isEmpty()) {
            return error("交易对不存在: " + symbol);
        }
        Long contractId = ((Number) coinRows.get(0).get("id")).longValue();

        // 查带单者活跃仓位 ID
        List<Long> traderPosIds = jdbcTemplate.queryForList(
                "SELECT id FROM member_contract_position" +
                " WHERE member_id = ? AND contract_id = ? AND status != 2 AND follow_position_id IS NULL",
                Long.class, traderMemberId, contractId);

        if (traderPosIds.isEmpty()) {
            log.info("[CopyTradePosition] 无活跃仓位 traderMemberId={} symbol={}", traderMemberId, symbol);
            return success("无活跃仓位，无需平仓");
        }

        int sentCount = 0, failCount = 0;

        // 平带单者自己的仓位
        for (Long posId : traderPosIds) {
            try {
                restTemplate.postForObject(
                    "http://contract-swap-api/swap/admin/force-close-position?positionId=" + posId,
                    null, String.class);
                sentCount++;
            } catch (Exception e) {
                log.error("[CopyTradePosition] 平仓失败 posId={}: {}", posId, e.getMessage());
                failCount++;
            }
        }

        // 平跟单者仓位
        for (Long traderPosId : traderPosIds) {
            List<Long> followPosIds = jdbcTemplate.queryForList(
                    "SELECT id FROM member_contract_position" +
                    " WHERE follow_position_id = ? AND status != 2",
                    Long.class, traderPosId);
            for (Long followPosId : followPosIds) {
                try {
                    restTemplate.postForObject(
                        "http://contract-swap-api/swap/admin/force-close-position?positionId=" + followPosId,
                        null, String.class);
                    sentCount++;
                } catch (Exception e) {
                    log.error("[CopyTradePosition] 跟单平仓失败 posId={}: {}", followPosId, e.getMessage());
                    failCount++;
                }
            }
        }

        log.info("[CopyTradePosition] 强平操作完成 traderMemberId={} symbol={} 成功={} 失败={} by={}",
                traderMemberId, symbol, sentCount, failCount, username);
        return success("操作成功，已平仓 " + sentCount + " 条，失败 " + failCount + " 条");
    }

    private MessageResult emptyPage(int pageNo, int pageSize) {
        Map<String, Object> empty = new HashMap<>();
        empty.put("content", Collections.emptyList());
        empty.put("totalElements", 0);
        empty.put("pageNo", pageNo);
        empty.put("pageSize", pageSize);
        return MessageResult.getSuccessInstance("SUCCESS", empty);
    }

    private boolean resolveIsCoin(Long contractId, Long traderMemberId) {
        if (contractId == null || traderMemberId == null) return false;
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
                "SELECT money_type FROM member_contract_position" +
                " WHERE member_id = ? AND contract_id = ? AND follow_position_id IS NULL LIMIT 1",
                traderMemberId, contractId);
        if (!rows.isEmpty() && rows.get(0).get("money_type") != null) {
            return ((Number) rows.get(0).get("money_type")).intValue() == 1;
        }
        return false;
    }

    private BigDecimal fetchCurrentPrice(String symbol) {
        try {
            String url = "http://contract-swap-api/swap/symbol-price";
            ParameterizedTypeReference<Map<String, BigDecimal>> typeRef =
                    new ParameterizedTypeReference<Map<String, BigDecimal>>() {};
            ResponseEntity<Map<String, BigDecimal>> resp =
                    restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(null), typeRef);
            if (resp.getBody() != null) {
                Map<String, BigDecimal> body = resp.getBody();
                // API 可能用 "ETHUSDT" 或 "ETH/USDT"，两种格式都尝试
                BigDecimal price = body.get(symbol);
                if (price == null) price = body.get(symbol.replace("/", ""));
                return price != null ? price : BigDecimal.ZERO;
            }
        } catch (Exception e) {
            log.warn("[CopyTradePosition] 获取实时价格失败 symbol={}: {}", symbol, e.getMessage());
        }
        return BigDecimal.ZERO;
    }

    private BigDecimal toBD(Object v) {
        if (v == null) return BigDecimal.ZERO;
        if (v instanceof BigDecimal) return (BigDecimal) v;
        try { return new BigDecimal(v.toString()); } catch (Exception e) { return BigDecimal.ZERO; }
    }
}
