package cn.iocoder.yudao.module.report.controller.admin.goview;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.string.StringUtil;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.report.controller.admin.goview.vo.data.GoViewDataGetBySqlReqVO;
import cn.iocoder.yudao.module.report.controller.admin.goview.vo.data.GoViewDataRespVO;
import cn.iocoder.yudao.module.system.api.order.OrderApi;
import cn.iocoder.yudao.module.system.api.order.VO.OrderItemRespVO;
import cn.iocoder.yudao.module.system.api.order.VO.OrderItemTotalRespVO;
import cn.iocoder.yudao.module.system.api.order.VO.OrderRespVO;
import cn.iocoder.yudao.module.report.service.goview.GoViewDataService;
import cn.iocoder.yudao.module.report.controller.admin.goview.vo.data.OrderSubListGroupedRespVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


import javax.annotation.Resource;
import javax.validation.Valid;

import java.util.*;
import java.util.concurrent.TimeUnit;

import java.math.BigDecimal;
import java.math.RoundingMode;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;

import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;

@Tag(name = "管理后台 - GoView 数据", description = "提供 SQL、HTTP 等数据查询的能力")
@RestController
@RequestMapping("/report/go-view/data")
@Validated
public class GoViewDataController {

    private static final int SCALE_TWO = 2;

    private static BigDecimal scale2(BigDecimal v) {
        if (v == null) {
            return null;
        }
        return v.setScale(SCALE_TWO, RoundingMode.HALF_UP);
    }

    private static void scale2OrderItem(OrderItemRespVO vo) {
        if (vo == null) {
            return;
        }
        vo.setBedAmount(scale2(vo.getBedAmount()));
        vo.setMealAmount(scale2(vo.getMealAmount()));
        vo.setNurseAmount(scale2(vo.getNurseAmount()));
        vo.setServiceAmount(scale2(vo.getServiceAmount()));
        vo.setExtraServiceAmount(scale2(vo.getExtraServiceAmount()));
        vo.setTotalAmount(scale2(vo.getTotalAmount()));

        vo.setAdjustAmount(scale2(vo.getAdjustAmount()));

        vo.setBedActualAmount(scale2(vo.getBedActualAmount()));
        vo.setMealActualAmount(scale2(vo.getMealActualAmount()));
        vo.setNurseActualAmount(scale2(vo.getNurseActualAmount()));
        vo.setServiceActualAmount(scale2(vo.getServiceActualAmount()));
        vo.setTotalActualAmount(scale2(vo.getTotalActualAmount()));
    }

    private static void scale2OrderTotal(OrderItemTotalRespVO vo) {
        if (vo == null) {
            return;
        }
        vo.setBedTotalAmount(scale2(vo.getBedTotalAmount()));
        vo.setMealTotalAmount(scale2(vo.getMealTotalAmount()));
        vo.setNurseTotalAmount(scale2(vo.getNurseTotalAmount()));
        vo.setServiceTotalAmount(scale2(vo.getServiceTotalAmount()));
        vo.setExtraServiceAmount(scale2(vo.getExtraServiceAmount()));
        vo.setTotalAmount(scale2(vo.getTotalAmount()));

        vo.setAdjustTotalAmount(scale2(vo.getAdjustTotalAmount()));

        vo.setBedActualTotalAmount(scale2(vo.getBedActualTotalAmount()));
        vo.setMealActualTotalAmount(scale2(vo.getMealActualTotalAmount()));
        vo.setNurseActualTotalAmount(scale2(vo.getNurseActualTotalAmount()));
        vo.setServiceActualTotalAmount(scale2(vo.getServiceActualTotalAmount()));
        vo.setTotalActualAmount(scale2(vo.getTotalActualAmount()));
    }

    @Resource
    private GoViewDataService goViewDataService;

    @Resource
    private OrderApi orderApi;

    @Resource
    private CacheManager cacheManager;

    private static final String ORDER_SUB_CACHE_NAME = "report:goview:orderSubGrouped#30s";

    private OrderSubListGroupedRespVO loadOrComputeGrouped(Long tenantId, String billingMonth, Integer payStatus) {
        String cacheKey = tenantId + "|" + billingMonth + "|" + (payStatus == null ? "null" : payStatus);

        Cache cache = cacheManager.getCache(ORDER_SUB_CACHE_NAME);
        if (cache != null) {
            OrderSubListGroupedRespVO cached = cache.get(cacheKey, OrderSubListGroupedRespVO.class);
            if (cached != null) {
                return cached;
            }
        }

        List<OrderItemRespVO> listByMonth = orderApi.getOrderSubListForReport(null, tenantId, billingMonth, payStatus);
        OrderSubListGroupedRespVO grouped = new OrderSubListGroupedRespVO();
        if (!CollectionUtils.isEmpty(listByMonth)) {
            List<OrderItemRespVO> leave = new ArrayList<>();
            List<OrderItemRespVO> retreat = new ArrayList<>();
            List<OrderItemRespVO> in = new ArrayList<>();
            for (OrderItemRespVO vo : listByMonth) {
                String status = vo.getInStatus();
                if ("离住".equals(status)) {
                    leave.add(vo);
                } else if ("退住".equals(status)) {
                    retreat.add(vo);
                } else {
                    in.add(vo);
                }
            }
            grouped.setLeaveList(leave);
            grouped.setRetreatList(retreat);
            grouped.setInList(in);
        }

        if (cache != null) {
            cache.put(cacheKey, grouped);
        }
        return grouped;
    }

    @RequestMapping("/get-by-sql")
    @Operation(summary = "使用 SQL 查询数据")
    @PreAuthorize("@ss.hasPermission('report:go-view-data:get-by-sql')")
    public CommonResult<GoViewDataRespVO> getDataBySQL(@Valid @RequestBody GoViewDataGetBySqlReqVO reqVO) {
        return success(goViewDataService.getDataBySQL(reqVO.getSql()));
    }

    @RequestMapping("/get-by-http")
    @Operation(summary = "使用 HTTP 查询数据", description = "这个只是示例接口，实际应该每个查询，都要写一个接口")
    @PreAuthorize("@ss.hasPermission('report:go-view-data:get-by-http')")
    public CommonResult<GoViewDataRespVO> getDataByHttp(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) { // params、body 按照需要去接收，这里仅仅是示例
        GoViewDataRespVO respVO = new GoViewDataRespVO();
        // 1. 数据维度
        respVO.setDimensions(Arrays.asList("日期", "PV", "UV")); // PV 是每天访问次数；UV 是每天访问人数
        // 2. 明细数据列表
        // 目前通过随机的方式生成。一般来说，这里你可以写逻辑来实现数据的返回
        respVO.setSource(new LinkedList<>());
        for (int i = 1; i <= 12; i++) {
            String date = "2021-" + (i < 10 ? "0" + i : i);
            Integer pv = RandomUtil.randomInt(1000, 10000);
            Integer uv = RandomUtil.randomInt(100, 1000);
            respVO.getSource().add(MapUtil.<String, Object>builder().put("日期", date)
                    .put("PV", pv).put("UV", uv).build());
        }
        return success(respVO);
    }


    @RequestMapping("/getOrderList")
    @Operation(summary = "查询订单列表", description = "")
    public CommonResult<List<OrderRespVO>> getOrderList(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) { // params、body 按照需要去接收，这里仅仅是示例
        GoViewDataRespVO respVO = new GoViewDataRespVO();
        // 1. 数据维度
        respVO.setDimensions(Arrays.asList("日期", "PV", "UV")); // PV 是每天访问次数；UV 是每天访问人数
        // 2. 明细数据列表
        // 目前通过随机的方式生成。一般来说，这里你可以写逻辑来实现数据的返回
        respVO.setSource(new LinkedList<>());
        for (int i = 1; i <= 12; i++) {
            String date = "2021-" + (i < 10 ? "0" + i : i);
            Integer pv = RandomUtil.randomInt(1000, 10000);
            Integer uv = RandomUtil.randomInt(100, 1000);
            respVO.getSource().add(MapUtil.<String, Object>builder().put("日期", date)
                    .put("PV", pv).put("UV", uv).build());
        }
        OrderRespVO orderRespVO = OrderRespVO.builder()
                .billMonth("2024-08").build();
        List<OrderRespVO> list = new ArrayList<>();
        list.add(orderRespVO);
        OrderRespVO orderRespVO2 = OrderRespVO.builder()
                .billMonth("2024-09").build();
        list.add(orderRespVO2);
        return success(list);
    }

    @RequestMapping("/getOrderSubListIn")
    @Operation(summary = "查询在住/退住/离住，返回在住列表（缓存三类）", description = "")
    @TenantIgnore
    public CommonResult<List<OrderItemRespVO>> getOrderSubListIn(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) { // params、body 按照需要去接收，这里仅仅是示例
        String billingMonth = StringUtils.isBlank(params.get("billingMonth")) ? "2025-12" : params.get("billingMonth");
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        OrderSubListGroupedRespVO grouped = loadOrComputeGrouped(getTenantId(params), billingMonth, payStatus);
        if (!CollectionUtils.isEmpty(grouped.getInList())) {
            grouped.getInList().forEach(GoViewDataController::scale2OrderItem);
        }
        return success(grouped.getInList());
    }

    @RequestMapping("/getOrderSubListLeave")
    @Operation(summary = "查询离住列表（命中在住接口的缓存）", description = "")
    @TenantIgnore
    public CommonResult<List<OrderItemRespVO>> getOrderSubListLeave(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) {
        String billingMonth = StringUtils.isBlank(params.get("billingMonth")) ? "2025-12" : params.get("billingMonth");
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        OrderSubListGroupedRespVO grouped = loadOrComputeGrouped(getTenantId(params), billingMonth, payStatus);
        if (!CollectionUtils.isEmpty(grouped.getLeaveList())) {
            grouped.getLeaveList().forEach(GoViewDataController::scale2OrderItem);
        }
        return success(grouped.getLeaveList());
    }

    @RequestMapping("/getOrderSubListRetreat")
    @Operation(summary = "查询退住列表（命中在住接口的缓存）", description = "")
    @TenantIgnore
    public CommonResult<List<OrderItemRespVO>> getOrderSubListRetreat(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) {
        String billingMonth = StringUtils.isBlank(params.get("billingMonth")) ? "2025-12" : params.get("billingMonth");
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        OrderSubListGroupedRespVO grouped = loadOrComputeGrouped(getTenantId(params), billingMonth, payStatus);
        if (!CollectionUtils.isEmpty(grouped.getRetreatList())) {
            grouped.getRetreatList().forEach(GoViewDataController::scale2OrderItem);
        }
        return success(grouped.getRetreatList());
    }

    @RequestMapping("/getOrderSubTotal")
    @Operation(summary = "查询汇总", description = "")
    public CommonResult<List<OrderItemTotalRespVO>> getOrderSubTotal(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) { // params、body 按照需要去接收，这里仅仅是示例
        String billingMonth = params.get("billingMonth");
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        OrderItemTotalRespVO orderItemTotalRespVO = orderApi.getTotalByMonth(null, getTenantId(params), billingMonth,payStatus);
        scale2OrderTotal(orderItemTotalRespVO);
        List<OrderItemTotalRespVO> list = new ArrayList<>();
        list.add(orderItemTotalRespVO);
        return success(list);
    }

    @RequestMapping("/getOrderReceivableList")
    @Operation(summary = "查询订单列表", description = "")
    @TenantIgnore
    public CommonResult<List<OrderItemRespVO>> getOrderReceivableList(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) { // params、body 按照需要去接收，这里仅仅是示例
        String billingMonth = params.get("billingMonth");
        String queryFLag = params.get("queryFLag");
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        List <OrderItemRespVO> listByMonth = orderApi.getReceivableListByMonth(null, getTenantId(params), queryFLag, billingMonth,payStatus);
        if (!CollectionUtils.isEmpty(listByMonth)) {
            listByMonth.forEach(GoViewDataController::scale2OrderItem);
        }
        return success(listByMonth);
    }

    @RequestMapping("/getOrderReceivableTotal")
    @Operation(summary = "查询汇总", description = "")
    public CommonResult<List<OrderItemTotalRespVO>> getOrderReceivableTotal(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) { // params、body 按照需要去接收，这里仅仅是示例
        String billingMonth = params.get("billingMonth");
        List<OrderItemTotalRespVO> list = new ArrayList<>();
        if (null == billingMonth) return success(list);
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        OrderItemTotalRespVO orderItemTotalRespVO = orderApi.getOrderReceivableTotal(null, getTenantId(params), billingMonth,payStatus);
        scale2OrderTotal(orderItemTotalRespVO);
        list.add(orderItemTotalRespVO);
        return success(list);
    }


    // 新增：仅汇总在住（本地缓存聚合）
    @RequestMapping("/getOrderSubTotalIn")
    @Operation(summary = "查询在住汇总", description = "仅汇总在住记录 (本地缓存聚合)")
    public CommonResult<List<OrderItemTotalRespVO>> getOrderSubTotalIn(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) {
        String billingMonth = params.get("billingMonth");
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        OrderSubListGroupedRespVO grouped = loadOrComputeGrouped(getTenantId(params), billingMonth, payStatus);
        OrderItemTotalRespVO total = aggregate(grouped.getInList(), billingMonth);
        scale2OrderTotal(total);
        return success(java.util.Collections.singletonList(total));
    }

    // 新增：仅汇总离住
    @RequestMapping("/getOrderSubTotalLeave")
    @Operation(summary = "查询离住汇总", description = "仅汇总离住记录 (本地缓存聚合)")
    public CommonResult<List<OrderItemTotalRespVO>> getOrderSubTotalLeave(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) {
        String billingMonth = params.get("billingMonth");
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        OrderSubListGroupedRespVO grouped = loadOrComputeGrouped(getTenantId(params), billingMonth, payStatus);
        OrderItemTotalRespVO total = aggregate(grouped.getLeaveList(), billingMonth);
        scale2OrderTotal(total);
        return success(java.util.Collections.singletonList(total));
    }

    // 新增：仅汇总退住
    @RequestMapping("/getOrderSubTotalRetreat")
    @Operation(summary = "查询退住汇总", description = "仅汇总退住记录 (本地缓存聚合)")
    public CommonResult<List<OrderItemTotalRespVO>> getOrderSubTotalRetreat(
            @RequestParam(required = false) Map<String, String> params,
            @RequestBody(required = false) String body) {
        String billingMonth = params.get("billingMonth");
        Integer payStatus = params.get("payStatus") == null ? null : Integer.valueOf(params.get("payStatus"));
        OrderSubListGroupedRespVO grouped = loadOrComputeGrouped(getTenantId(params), billingMonth, payStatus);
        OrderItemTotalRespVO total = aggregate(grouped.getRetreatList(), billingMonth);
        scale2OrderTotal(total);
        return success(java.util.Collections.singletonList(total));
    }

    // Helper：本地聚合
    private OrderItemTotalRespVO aggregate(List<OrderItemRespVO> src, String billingMonth) {
        OrderItemTotalRespVO t = new OrderItemTotalRespVO();
        t.setBillingMonth(billingMonth);
        if (src == null || src.isEmpty()) {
            return t;
        }
        for (OrderItemRespVO vo : src) {
            t.setBedTotalAmount(t.getBedTotalAmount().add(nz(vo.getBedAmount())));
            t.setMealTotalAmount(t.getMealTotalAmount().add(nz(vo.getMealAmount())));
            t.setNurseTotalAmount(t.getNurseTotalAmount().add(nz(vo.getNurseAmount())));
            t.setServiceTotalAmount(t.getServiceTotalAmount().add(nz(vo.getServiceAmount())));
            t.setTotalAmount(t.getTotalAmount().add(nz(vo.getTotalAmount())));
            t.setAdjustTotalAmount(t.getAdjustTotalAmount().add(nz(vo.getAdjustAmount())));
            t.setBedActualTotalAmount(t.getBedActualTotalAmount().add(nz(vo.getBedActualAmount())));
            t.setMealActualTotalAmount(t.getMealActualTotalAmount().add(nz(vo.getMealActualAmount())));
            t.setNurseActualTotalAmount(t.getNurseActualTotalAmount().add(nz(vo.getNurseActualAmount())));
            t.setServiceActualTotalAmount(t.getServiceActualTotalAmount().add(nz(vo.getServiceActualAmount())));
            t.setTotalActualAmount(t.getTotalActualAmount().add(nz(vo.getTotalActualAmount())));
            t.setExtraServiceAmount(t.getExtraServiceAmount().add(nz(vo.getExtraServiceAmount())));
        }
        if (t.getTotalAmount().compareTo(java.math.BigDecimal.ZERO) > 0) {
            java.math.BigDecimal p = t.getTotalActualAmount().divide(t.getTotalAmount(),2,java.math.RoundingMode.HALF_UP).multiply(new java.math.BigDecimal("100"));
            p = p.setScale(0, java.math.RoundingMode.HALF_UP);
            t.setActualTotalProportion(p.toString()+"%");
        } else {
            t.setActualTotalProportion("0");
        }
        return t;
    }

    private java.math.BigDecimal nz(java.math.BigDecimal v){return v==null?java.math.BigDecimal.ZERO:v;}

    private Long getTenantId(Map<String, String> params){
        if(params.get("tenantId") == null){
            return 1L;
        }
        String tenantId = params.get("tenantId");
        String[] split = tenantId.split(",");
        if(split.length == 1){
            return Long.parseLong(split[0]);
        }else {
            return Long.parseLong(split[1]);
        }
    }
}
