package com.snack.server.module.dashboard.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.snack.server.module.dashboard.service.DashboardService;
import com.snack.server.module.dashboard.vo.DashboardStatsVO;
import com.snack.server.module.dashboard.vo.NameValueVO;
import com.snack.server.module.dashboard.vo.SalesTrendVO;
import com.snack.server.module.order.entity.Order;
import com.snack.server.module.order.enums.OrderStatusEnum;
import com.snack.server.module.order.mapper.OrderMapper;
import com.snack.server.module.product.entity.Product;
import com.snack.server.module.product.mapper.ProductMapper;
import com.snack.server.module.user.entity.User;
import com.snack.server.module.user.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 仪表盘统计业务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DashboardServiceImpl implements DashboardService {

    private static final DateTimeFormatter DAY_FMT = DateTimeFormatter.ofPattern("MM-dd");
    private static final DateTimeFormatter SQL_DAY_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /** 已支付订单状态（参与销售额统计，剔除未支付/取消） */
    private static final List<Integer> PAID_STATUSES = List.of(1, 2, 3, 5);

    private final OrderMapper orderMapper;
    private final UserMapper userMapper;
    private final ProductMapper productMapper;

    @Override
    public DashboardStatsVO stats() {
        LocalDateTime todayStart = LocalDate.now().atStartOfDay();

        // 今日订单数
        Long todayOrders = orderMapper.selectCount(
                new LambdaQueryWrapper<Order>().ge(Order::getCreateTime, todayStart));

        // 今日销售额：按支付时间当天、已支付订单
        List<Map<String, Object>> salesRows = orderMapper.selectMaps(
                new QueryWrapper<Order>()
                        .select("IFNULL(SUM(pay_amount), 0) AS todaySales")
                        .ge("pay_time", todayStart)
                        .in("status", PAID_STATUSES));
        BigDecimal todaySales = salesRows.isEmpty()
                ? BigDecimal.ZERO
                : toBigDecimal(salesRows.get(0).get("todaySales"));

        // 总用户数
        Long totalUsers = userMapper.selectCount(null);

        // 上架商品数
        Long totalProducts = productMapper.selectCount(
                new LambdaQueryWrapper<Product>().eq(Product::getStatus, 1));

        DashboardStatsVO vo = new DashboardStatsVO();
        vo.setTodayOrders(todayOrders);
        vo.setTodaySales(todaySales);
        vo.setTotalUsers(totalUsers);
        vo.setTotalProducts(totalProducts);
        return vo;
    }

    @Override
    public List<SalesTrendVO> salesTrend(int days) {
        if (days <= 0) {
            days = 7;
        }
        days = Math.min(days, 90);

        LocalDate startDate = LocalDate.now().minusDays(days - 1L);
        LocalDateTime startTime = startDate.atStartOfDay();

        // SQL 内按天聚合销售额（已支付订单）
        List<Map<String, Object>> rows = orderMapper.selectMaps(
                new QueryWrapper<Order>()
                        .select("DATE_FORMAT(pay_time, '%Y-%m-%d') AS statDate",
                                "IFNULL(SUM(pay_amount), 0) AS amount")
                        .ge("pay_time", startTime)
                        .in("status", PAID_STATUSES)
                        .groupBy("statDate")
                        .orderByAsc("statDate"));

        Map<String, BigDecimal> byDate = new LinkedHashMap<>();
        for (Map<String, Object> row : rows) {
            Object dateVal = row.get("statDate");
            Object amountVal = row.get("amount");
            if (dateVal == null) continue;
            String date = dateVal.toString();
            byDate.put(date, toBigDecimal(amountVal));
        }

        // 补全缺失日期为 0，保证前端折线图连续
        List<SalesTrendVO> result = new ArrayList<>();
        for (int i = 0; i < days; i++) {
            LocalDate d = startDate.plusDays(i);
            String sqlKey = d.format(SQL_DAY_FMT);
            SalesTrendVO vo = new SalesTrendVO();
            vo.setDate(d.format(DAY_FMT));
            vo.setAmount(byDate.getOrDefault(sqlKey, BigDecimal.ZERO));
            result.add(vo);
        }
        return result;
    }

    @Override
    public List<NameValueVO> orderStatus() {
        // 一次分组查询，遍历枚举映射 desc，缺失状态补 0（保证饼图图例稳定）
        List<Map<String, Object>> rows = orderMapper.selectMaps(
                new QueryWrapper<Order>()
                        .select("status", "COUNT(*) AS cnt")
                        .groupBy("status"));

        Map<Integer, Long> countByStatus = new LinkedHashMap<>();
        for (Map<String, Object> row : rows) {
            Object statusVal = row.get("status");
            Object cntVal = row.get("cnt");
            if (statusVal == null) continue;
            int status = Integer.parseInt(statusVal.toString());
            long cnt = cntVal == null ? 0L : Long.parseLong(cntVal.toString());
            countByStatus.put(status, cnt);
        }

        List<NameValueVO> result = new ArrayList<>();
        for (OrderStatusEnum e : OrderStatusEnum.values()) {
            NameValueVO vo = new NameValueVO();
            vo.setName(e.getDesc());
            vo.setValue(countByStatus.getOrDefault(e.getCode(), 0L));
            result.add(vo);
        }
        return result;
    }

    @Override
    public List<NameValueVO> hotProducts() {
        List<Product> products = productMapper.selectList(
                new LambdaQueryWrapper<Product>()
                        .eq(Product::getStatus, 1)
                        .orderByDesc(Product::getSales)
                        .last("LIMIT 10"));

        List<NameValueVO> result = new ArrayList<>();
        for (Product p : products) {
            NameValueVO vo = new NameValueVO();
            vo.setName(p.getName());
            vo.setValue(p.getSales() == null ? 0 : p.getSales());
            result.add(vo);
        }
        return result;
    }

    private BigDecimal toBigDecimal(Object val) {
        if (val == null) return BigDecimal.ZERO;
        if (val instanceof BigDecimal bd) return bd;
        return new BigDecimal(val.toString());
    }
}
